Compare commits

...

138 Commits
4.0.7 ... main

Author SHA1 Message Date
云荷YunHerry 27acc9a80e
docs(table): ajax demo (#8120)
Co-authored-by: Xian Yu <h3209851541@outlook.com>
2025-06-13 15:46:47 +08:00
selicens 43ad5dec90
docs(mentions): Spelling error in En title (#8127) 2025-06-13 15:45:49 +08:00
selicens aa8802679f
docs(Typography): fix slot name (#8184) 2025-06-13 15:35:05 +08:00
爆炒芋头 aa211fd789
docs(input): showCount type 文档 (#8041)
* fix(input): showCount type 文档

* fix(input): showCount type 文档

---------

Co-authored-by: zhiqiangchen <zhiqiang.chen@igg.com>
2025-03-06 14:37:19 +08:00
tangjinzhou 61d39be86f
Update config.yml 2025-03-06 14:34:53 +08:00
tangjinzhou b80bd37f6e fix: sm select placeholder 2025-02-17 11:28:06 +08:00
wzc520pyfm 3db5e191b8
docs: add ant-design-x-vue link at ecosystem (#8034) 2025-02-09 09:23:58 +08:00
Attacktive 45c7109212
docs: correct possible `size` size in component Select (#7991) 2025-01-05 17:04:07 +08:00
Shai b58d0fb723
docs: added centered to docs (#7967) 2025-01-05 16:55:58 +08:00
clddup a21a1ca533
docs: theme editor 2025-01-05 16:54:32 +08:00
tangjinzhou 4a37016f4e fix: ts type error 2024-11-11 14:52:27 +08:00
tangjinzhou 5d6ebb30ac release 4.2.6 2024-11-11 14:47:34 +08:00
tangjinzhou 888457238d fix: modal aria error 2024-11-11 14:40:49 +08:00
果冻橙 b0d9309471
docs: Add custom icons to support Rsbuild documentation (#7743) 2024-11-08 10:30:05 +08:00
selicens f64d0718ae
docs: Change the default values of attributes (#7744) 2024-11-08 10:28:03 +08:00
selicens 968317ca9f
fix(InputNumber): disabled text clolor (#7776) 2024-11-08 10:18:03 +08:00
Carl Chen 22dad3ba6d
fix[Select]: fix select losing focus issue (#7829) 2024-11-08 10:17:22 +08:00
iamnotblank 4f7bd6f28d
fix(Dialog): aira error (#7823)
* fix(vc-dialog): aira error

* fix(vc-dialog): aira error
2024-11-07 22:54:53 +08:00
snoweast 12fcfa15b1
fix: Safari of input IME (#7918) 2024-11-07 22:45:48 +08:00
tangjinzhou e46d537d45 release 4.2.5 2024-09-19 19:08:59 +08:00
tangjinzhou ac9d1b0a7f fix: empty memory issue 2024-09-19 19:07:08 +08:00
tangjinzhou 2c7008d786 fix: image width & height not work 2024-09-19 19:06:34 +08:00
tangjinzhou 49d4b3166e feat: update type 2024-09-13 19:33:38 +08:00
tangjinzhou 339fb4a230 release 4.2.4 2024-09-13 19:06:37 +08:00
tangjinzhou bb443a05e2 fix: button wave memory leak 2024-09-13 19:05:04 +08:00
YYP 3a79f72816
fix(doc): `positonStyle` typo (#7808) 2024-09-13 18:51:17 +08:00
5918vo 89eec07145
docs: event demo (#7699) (#7728) 2024-09-05 11:38:10 +08:00
Jason Ren c69a6d29b2
docs(zh-cn): finish translation of float-button (#7811) 2024-09-05 11:37:36 +08:00
tangjinzhou 2666cb79ab refactor: transition 2024-06-21 13:40:28 +08:00
tangjinzhou 35d5185634 docs: update changelog 2024-06-10 09:37:38 +08:00
tangjinzhou 4492086aac release 4.2.3 2024-06-10 09:20:15 +08:00
trry-hub 4b7c6ac02c
feat: TourStep support fn children (#7628)
* Fix: [修复自定义按钮无渲染]

* refactor(完善 Tour 自定义按钮代码健壮性): 🦄

---------

Co-authored-by: shizhu <shizhu@yaomaitong.cn>
2024-06-10 09:14:27 +08:00
Kylin 208b8d3085
docs: unify the default value of none to - (#7636) 2024-06-09 22:08:30 +08:00
Carl Chen f41fec26ba
fix[vc-util]: styleObjectToString filter invalid value (#7642) 2024-06-09 09:56:45 +08:00
tangjinzhou 9118d6cd42 release 4.2.2 2024-06-06 10:16:02 +08:00
Aaron-zon 5c7aaf0dd6
fix(FloatButton): type error (#7632) 2024-06-06 10:03:24 +08:00
evanlc b82d8dd2ad
fix(PresetPanel): preset panel stopPropagation (#7550) 2024-06-04 23:28:22 +08:00
Aaron-zon 1d0fa8533a
fix: add missing space-evenly alignment for grid rows (#7579) 2024-06-04 23:26:03 +08:00
Kylin 4318147fc6
refactor(input): remove unnecessary assertions and use?? instead of the ternary operator (#7571)
* refactor: remove unnecessary assertions and use?? instead of the ternary operator

* refactor: use?? instead of the ternary operator
2024-06-04 23:24:44 +08:00
Carl Chen 312bcc5127
docs(collapse): modify the `accordion` attribute text to be easier to understand (#7612) 2024-06-04 23:20:54 +08:00
tangjinzhou 6594fe3964 fix: textarea 2024-06-04 23:12:06 +08:00
tangjinzhou 82f28ce3d0 fix: select input 2024-06-04 21:20:10 +08:00
Carl Chen 6e2c5a6a83
fix[select]: fix placeholder error when inputting Chinese (#7611)
* fix[select]: fix placeholder error when inputting Chinese

fix(selector): fix placeholder error when inputting Chinese

perf: optimize types

* Update SingleSelector.tsx

* Update index.tsx

---------

Co-authored-by: tangjinzhou <415800467@qq.com>
2024-06-04 18:59:24 +08:00
lchrennew 307148e2df
fix: empty space-item are not been hidden (#7557) 2024-06-04 16:13:11 +08:00
bsuooo afebeb9ffa
docs(DatePicker): updating deprecated API (#7570) 2024-06-04 16:12:20 +08:00
selicens aeda2637f6
fix(Input): Chinese input is invalid when the modifier is lazy (#7543) 2024-06-04 16:10:46 +08:00
extclp f42d8ad1ce
chore: remove css-animation (#7613)
* chore: remove css-animate

* chore(site): remove localStorage support check
2024-06-04 16:05:21 +08:00
selicens 33a0708eb4
docs: upgrade document link direction error (#7602) 2024-06-04 16:01:26 +08:00
selicens 085eb398a4
fix(FormItem): class name error (#7582) 2024-06-04 16:00:40 +08:00
selicens 9c27414cd0
fix(FloatButton): type prompt error (#7576) 2024-06-04 15:57:40 +08:00
Kylin 014e86bd27
docs: update README-zh_CN (#7584) 2024-06-04 15:49:47 +08:00
tangjinzhou 40ad45bc05 fix: tree reative data error 2024-06-04 15:46:22 +08:00
neverland 1c82940160
docs: add Vite and Rsbuild to getting started (#7556) 2024-05-09 21:21:20 +08:00
bsuooo 7ce0f115d5
fix(cascader): remove outdated compatibility code(#7532) (#7534) 2024-04-30 19:13:48 +08:00
extclp 54434b0931
docs(Select): remove qs dependencie (#7541) 2024-04-30 19:12:05 +08:00
selicens e01f26c541
fix(TreeSelect): placehold slot invalid (#7545) 2024-04-30 19:11:08 +08:00
tangjinzhou 9a45b35511 release 4.2.1 2024-04-25 09:40:07 +08:00
hippo 8e8073d17e
fix: input clear error (#7523) 2024-04-25 09:29:38 +08:00
extclp d6cc262c3a
refactor: replement deprecated fields (#7519) 2024-04-25 09:25:29 +08:00
tangjinzhou 9d7c171940 release 4.2.0 2024-04-22 15:51:05 +08:00
extclp 48a3ceb921
chore: remove vetur type generator (#7517) 2024-04-22 15:27:58 +08:00
tangjinzhou 23c620ea3a fix: select customicon error #7377 2024-04-22 15:26:02 +08:00
tangjinzhou 752686e334 fix: input composing error, close #7516 2024-04-22 15:11:10 +08:00
tangjinzhou 9b0f0e71e7 perf: table hover cell, close #7451 2024-04-22 10:37:28 +08:00
24min 42d33e963c
fix: carousel beforeChange current value not correct (#7419) 2024-04-22 10:18:19 +08:00
tangjinzhou ed27700ef4 style: lint code 2024-04-22 10:07:02 +08:00
extclp eedd7f3302
chore: remove json file type declare (#7514) 2024-04-22 10:00:03 +08:00
Carl Chen c28c38d02e
fix[Select|Cascader]: select multiple error & cascader error in ssr mode (#7377)
* docs: updating the `dropdownRender` description and jumps in the FAQ for Select

* fix: fix  select error in multiple mode

* fix: fix cascader select error in ssr mode
2024-04-19 22:56:07 +08:00
ashu-guo 162d1fcf95
fix(AutoComplete): #7380 and #7276 (#7391) 2024-04-19 22:46:19 +08:00
selicens 4815ee6f20
fix(Pagination): block default events for the enter key (#7368)
* fix(Pagination): block default events for the enter key

* refactor: consider other keyboard events
2024-04-19 22:44:34 +08:00
bqy_fe 2b41e56520
fix(Input): autoSize not work when change value in onMounted (#7478) 2024-04-19 22:36:44 +08:00
Sean ab874ffd4b
docs: update tree-select api description (#7388) 2024-04-19 22:07:41 +08:00
tangjinzhou 61ade6b8ec fix: autoComplete option slot error, close #7396 #7405 2024-04-19 19:27:51 +08:00
selicens 4d35b8caa3
docs(Modal): add simplified descriptions (#7408)
close #7393
2024-04-19 19:19:33 +08:00
Tang 040af82eb9
docs: update table docs (#7400)
Co-authored-by: 汤显文 <tang_xianwen@gov-info.cn>
2024-04-19 19:19:01 +08:00
Carl Chen a8c72fc5e7
fix[Menu]: fix `menu-item-group` not rendering in SSR (#7349)
* docs: updating the `dropdownRender` description and jumps in the FAQ for Select

* fix: `menu-item-group` rendered in ssr
2024-04-19 19:18:30 +08:00
ashu-guo c25736d57d
chore: merge duplicate property declarations (#7417) 2024-04-19 19:16:46 +08:00
SonicKang 4a48bfc66b
fix: a-qrcord component cannot show ts prompt (#7502) 2024-04-19 19:15:12 +08:00
tangjinzhou 4138d3c822 fix: tabs size error, close #7491 #7482 2024-04-19 19:14:18 +08:00
tangjinzhou dec67a6d65 docs: update site 2024-04-19 19:13:06 +08:00
Carl Chen 85c48c0566
feat[tooltip]: add `arrow` attribute (#7459)
* docs: updating the `dropdownRender` description and jumps in the FAQ for Select

* wip: add popover-arrow

* wip: trigger add arrow attr

* fix: remove popupContextKey

* optimize

* perf: optimize

* docs: optimize docs

* docs: add `arrow` attribute in tooltip en-US docs

* fix: fix bug

* perf[demo]: `radio-group` replace with `segmented`
2024-04-19 13:17:15 +08:00
Carl Chen 966bc1004c
fix[layout]: in a dark mode layout, the text color should change. (#7498)
* docs: updating the `dropdownRender` description and jumps in the FAQ for Select

* fix[layout]: in a dark mode layout, the text color should change
2024-04-19 10:25:23 +08:00
Ordinary 457d0fde0b
fix: remove duplicate style variable (#7490)
Co-authored-by: p-hehongsheng <p-hehongsheng@xiaomi.com>
2024-04-19 10:21:07 +08:00
Saveliy ff184b3969
feat: update ru_RU localization (#7497) 2024-04-19 10:20:15 +08:00
ashu-guo 75886a8cbf
docs: match dependencies in package.json instead of simply setting 'latest' (#7381) 2024-04-19 10:19:50 +08:00
fwd01 2b0c2da232
docs: typo error (#7505)
Co-authored-by: fuwedong <fuwendong5@outlook.com>
2024-04-19 10:18:59 +08:00
tangjinzhou ffd4d8fe92 fix: useForm model change validate error 2024-04-19 10:18:04 +08:00
tangjinzhou 49e1323baa docs: update codesandbox 2024-04-19 09:52:43 +08:00
tangjinzhou 35d1de9ea6 docs: update sandbox 2024-04-19 09:29:05 +08:00
huyikai fa46999963
Update ja_JP.ts (#7438) 2024-03-23 10:40:28 +08:00
tangjinzhou 01300d01da docs: add eth sponsor 2024-03-22 16:20:10 +08:00
tangjinzhou 9a5f83ed06 chore: update pkg 2024-03-03 14:09:19 +08:00
tangjinzhou ce6b7f3b4e chore: update pkg 2024-03-03 14:04:52 +08:00
tangjinzhou 4a2f95fe88 release 4.1.2 2024-01-30 12:19:46 +08:00
Carl Chen 7c73beb309
fix: fixed an error caused by dragging under the `headerCell` slot. (#7291)
* fix: fix headerCell slots

* perf: optimize table columnTitle type
2024-01-30 12:06:45 +08:00
Carl Chen 0cbf3ca354
docs: updating the `dropdownRender` description and jumps in the FAQ for Select (#7313) 2024-01-30 12:05:20 +08:00
Weite Su 7afb7ce465
fix(segmented): title property in segmented component slot not effective (#7302)
Co-authored-by: suweite <weite.su@infinigo.cn>
2024-01-30 12:04:24 +08:00
tangjinzhou 9cc73011c5 chore: import error 2024-01-16 21:23:36 +08:00
tangjinzhou 4b21210700 release 4.1.1 2024-01-16 21:10:05 +08:00
Carl Chen a80ca17461
fix: table slot type optimization (#7288) 2024-01-16 20:57:29 +08:00
HaiWei Lian ad8d32ab09
fix: table filter does not work in header group (#7233) 2024-01-16 20:56:47 +08:00
Carl Chen d870f3f8e0
fix: fixed error with no expected value in `expandColumnTitle` slot (#7265)
* fix: fixed error report with no expected value in `expandColumnTitle` slot

* fix: optimize optional chain

* fix: use default render

* refactor: use `customRenderSlot` replace `renderSlot`

* style: code format

* perf: optimize useColumns code

* fix: fix path

* feat: add customRenderSlot unit test
2024-01-16 20:49:38 +08:00
tangjinzhou c717473568 fix: hook warning, close #7281 #7273 #7274 2024-01-16 20:34:11 +08:00
selicens 1e07544e74
feat(QRCode): support scanned status (#7242) 2024-01-12 11:44:33 +08:00
Light 8ab008d255
typo(table): Supplement FilterDropdown type (#7226)
* fix(table): Supplement FilterDropdown type

* docs(table): Type format
2024-01-12 11:42:46 +08:00
一堆菠萝 f034a7759e
fix(divider): vertical dashed divider not show (#7218) 2024-01-12 10:50:06 +08:00
Konv Suu 3f33cefa81
ci: issue-labeled format error (#7200) 2024-01-12 10:45:46 +08:00
一堆菠萝 81e43c56ef
docs(typography): trigerType api supplement Chinese translation (#7228) 2024-01-12 10:45:09 +08:00
aShu-guo f400c803e1
docs: update modal api description (#7232)
* docs: update modal api description

* docs: update modal api description
2024-01-12 10:44:48 +08:00
somethingfornothing 502c11cc4c
fix(dropdown): unexpectedly hidden(#7246) (#7254) 2024-01-12 10:42:36 +08:00
markthree 2cdb69f706
faet: fix css prefix in nuxt generate (#7256) 2024-01-12 10:30:17 +08:00
tangjinzhou 2eed8e62ec fix: type error 2024-01-05 19:28:55 +08:00
tangjinzhou 61c0d0cc9d release 4.1.0 2024-01-05 16:22:56 +08:00
tangjinzhou d7f9bd27dc fix: support vue 3.4 2024-01-05 16:20:05 +08:00
cc heart 4ed2868137
fix(vue): fixed modal component failed to trigger update in vue@3.4.x (#7244)
* fix(vue): fixed modal component failed to trigger update in vue@3.4.x version

* refactor: use cloneVNode trigger vNode update
2024-01-05 09:11:25 +08:00
tangjinzhou 8696e01039 release 4.0.8 2023-12-18 16:19:36 +08:00
tangjinzhou a3fd390619 docs: update demo 2023-12-18 16:17:43 +08:00
tangjinzhou a9198e44df fix: theme reactive not work #7180 2023-12-18 15:34:54 +08:00
yanyu 509ec682f2
fix: Fix custom theme token (#7180)
* fix: fix table column data is passed into chlidren is undefined or null errorr

* fix: fix custom theme token not take effect

* chore: reset button style

---------

Co-authored-by: undefined <undefined>
2023-12-18 15:20:24 +08:00
tangjinzhou 73f0a29acf fix: textarea cursor error, close #7121 2023-12-18 14:31:53 +08:00
somethingfornothing f93dd9170d
fix: dataRange active-bar (#7157) 2023-12-18 11:37:38 +08:00
yang cfa0a68568
fix(Menu.SubMenu): 修复ConfigProvider组件设置prefixCls属性后动画过渡失效 (#7150) 2023-12-18 11:32:38 +08:00
Konv Suu afcff32fcc
fix(menu): pass motion name to collapseMotion (#7130) 2023-12-18 11:31:57 +08:00
tangjinzhou b989cf2d97 perf: watermark 2023-12-18 11:31:04 +08:00
Jevin 0e6fd652b8
fix(watermark): modify dom properties to regenerate (#7149)
Co-authored-by: “Jevin” <jevin@mogul-tech.com>
2023-12-18 11:29:27 +08:00
selicens 6625d39118
fix(RangPicker): Incorrect display of prevIcon and nextIcon (#7127)
* fix(RangPicker): Incorrect display of prevIcon and nextIcon

* fix: superPrevIcon and superNextIcon invalid
2023-12-18 11:23:30 +08:00
Nined 67efafca4a
perf(Typography): fast and efficient syncEllipsis (#7146) 2023-12-18 11:21:55 +08:00
OliverYoung a8a774a5ff
fix: tooltip onPopupAlign never called (#7112) 2023-12-18 10:54:30 +08:00
一堆菠萝 1fc109704b
fix: Form disabled no effect on Upload (#7110)
* fix: Form disabled no effect on Upload

* docs: update Upload disabled api default value
2023-12-18 10:45:27 +08:00
puppetkkk d140523c89
fix: useWave unref (#7108)
* fix: useWave unref

* fix: wave init value

* fix: wave value

* Update useConfigInject.ts

---------

Co-authored-by: pangzebang <pangzebang@dm-ai.com>
Co-authored-by: tangjinzhou <415800467@qq.com>
2023-12-18 10:41:50 +08:00
cc heart 91fe1b0d71
docs(menu): repair Anchor Jump (#7178)
Under the menu document, some anchor clicks cannot scroll.
2023-12-18 10:27:48 +08:00
lyn c1b4941def
docs: fix typo (#7162) 2023-12-18 10:27:22 +08:00
puppetkkk af383a34bc
docs(config-provider): add wave setting (#7107)
* docs(config-provider): add wave setting

* docs(config-provider): add wave setting

---------

Co-authored-by: pangzebang <pangzebang@dm-ai.com>
2023-12-18 10:25:23 +08:00
cc heart 1dccce4e16
docs(header): restore button hover style (#7105) 2023-12-18 10:24:56 +08:00
cc heart 8aa8e5a778
docs(calendar): optimize calendar docs note layout (#7102)
docs(calendar): optimize calendar docs note layout

docs(calendar): optimize calendar docs note layout

docs(calendar): optimize calendar docs note layout

docs(calendar): optimize calendar docs note layout
2023-12-18 10:24:18 +08:00
Light 42952b713a
docs(notification): Fix hooks description in demo (#7191) 2023-12-18 10:22:49 +08:00
yang 07c36192b8
【docs】:Code example 'setup' is missing (#7153)
* flx

* flx【docs】:Modify the official website code example, switch TS to JS, the top of the example should be '<script setup>'
2023-12-18 10:22:04 +08:00
Jacky 562623c091
fix: remove /* #__PURE__*/ comments (#7156) 2023-12-18 10:21:15 +08:00
220 changed files with 215362 additions and 13042 deletions

View File

@ -27,6 +27,7 @@ module.exports = {
// ], // ],
plugins: ['markdown', 'jest', '@typescript-eslint', 'import'], plugins: ['markdown', 'jest', '@typescript-eslint', 'import'],
globals: { globals: {
h: true,
defineProps: 'readonly', defineProps: 'readonly',
}, },
overrides: [ overrides: [
@ -108,7 +109,4 @@ 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: false blank_issues_enabled: true
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/

View File

@ -14,7 +14,7 @@ jobs:
pull-requests: write # for actions-cool/issues-helper to update PRs 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@v3
with: with:

View File

@ -10,6 +10,88 @@
--- ---
## 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 ## 4.0.7
- 🌟 Added Flex component [#7052](https://github.com/vueComponent/ant-design-vue/issues/7052) - 🌟 Added Flex component [#7052](https://github.com/vueComponent/ant-design-vue/issues/7052)

View File

@ -10,6 +10,88 @@
--- ---
## 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 ## 4.0.7
- 🌟 新增 Flex 组件 [#7052](https://github.com/vueComponent/ant-design-vue/issues/7052) - 🌟 新增 Flex 组件 [#7052](https://github.com/vueComponent/ant-design-vue/issues/7052)

View File

@ -10,7 +10,7 @@
<div align="center"> <div align="center">
An enterprise-class UI components based on Ant Design and Vue 3. 基于 Ant Design 和 Vue 3 的企业级 UI 组件库。
![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,6 +73,7 @@ $ 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,22 +89,23 @@ ant-design-vue 是 MIT 协议的开源项目。为了项目能够更好的持续
- [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://aliyuncdn.antdv.com/alipay-and-wechat.png)
- ETH: 0x30cc48515d8ae9fefa20ab87226ad7e8ab9c3bc2
## Sponsors ## 赞助商
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/ant-design-vue#sponsor)] 成为赞助商,并在 Github 上的自述文件上获得您的徽标,并链接到您的网站。 [[成为赞助商](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 ## 支持者
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/ant-design-vue#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
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://www.patreon.com/tangjinzhou)] 每月捐款支持我们,帮助我们继续我们的活动。 [[成为支持者](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

@ -73,6 +73,7 @@ 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,6 +83,7 @@ ant-design-vue is an MIT-licensed open source project. In order to achieve bette
- [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://aliyuncdn.antdv.com/alipay-and-wechat.png)
- ETH: 0x30cc48515d8ae9fefa20ab87226ad7e8ab9c3bc2
## Sponsors ## Sponsors

View File

@ -6,7 +6,6 @@ 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>> {
@ -22,13 +21,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: Map<String, VueTag> = new Map(); const tags = new Map<String, VueTag>();
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: Map<String, VueTag> = new Map(); const tags = new Map<String, VueTag>();
const fileContent = readFileSync(options.typingsPath, 'utf-8'); const fileContent = readFileSync(options.typingsPath, 'utf-8');
fileContent fileContent
.split('\n') .split('\n')
@ -62,7 +61,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: Map<String, VueTag> = new Map(); const tags = new Map<String, VueTag>();
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));
@ -78,13 +77,6 @@ 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.substr(0, end !== -1 ? end : input.length); return input.substring(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.substr(start); const target = input.substring(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.substr(start); const target = input.substring(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.substr(match.index)); const { table, usedLength } = tableParse(target.substring(match.index));
artical.push({ artical.push({
type: 'table', type: 'table',
table, table,

View File

@ -34,25 +34,6 @@ 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

@ -1,30 +0,0 @@
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,7 +1,6 @@
'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 () {
@ -9,7 +8,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 assign( return Object.assign(
{ {
noUnusedParameters: true, noUnusedParameters: true,
noUnusedLocals: true, noUnusedLocals: true,

View File

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

View File

@ -0,0 +1,96 @@
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

@ -7,7 +7,6 @@ import {
onMounted, onMounted,
onBeforeUnmount, onBeforeUnmount,
onUpdated, onUpdated,
getCurrentInstance,
nextTick, nextTick,
computed, computed,
} from 'vue'; } from 'vue';
@ -61,6 +60,7 @@ 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,8 +106,6 @@ export default defineComponent({
attachToParent(); attachToParent();
}); });
const instance = getCurrentInstance();
useScrollLocker( useScrollLocker(
computed(() => { computed(() => {
return ( return (
@ -155,7 +153,7 @@ export default defineComponent({
nextTick(() => { nextTick(() => {
if (!attachToParent()) { if (!attachToParent()) {
rafId.value = raf(() => { rafId.value = raf(() => {
instance.update(); triggerUpdate.value += 1;
}); });
} }
}); });
@ -177,7 +175,7 @@ export default defineComponent({
getOpenCount: () => openCount, getOpenCount: () => openCount,
getContainer, getContainer,
}; };
if (forceRender || visible || componentRef.value) { if (triggerUpdate.value && (forceRender || visible || componentRef.value)) {
portal = ( portal = (
<Portal <Portal
getContainer={getContainer} getContainer={getContainer}

View File

@ -0,0 +1,11 @@
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

@ -0,0 +1,26 @@
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

@ -1,42 +0,0 @@
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

@ -1,130 +0,0 @@
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

@ -1,186 +0,0 @@
// 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

@ -8,9 +8,13 @@ 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 = process.env.NODE_ENV !== 'production' ? 'css-dev-only-do-not-override' : 'css'; const hashPrefix = !isProduction && !isPrerender ? 'css-dev-only-do-not-override' : 'css';
export interface Option<DerivativeToken, DesignToken> { export interface Option<DerivativeToken, DesignToken> {
/** /**
@ -90,7 +94,6 @@ export const getComputedToken = <DerivativeToken = object, DesignToken = Derivat
format?: (token: DesignToken) => DerivativeToken, format?: (token: DesignToken) => DerivativeToken,
) => { ) => {
const derivativeToken = theme.getDerivativeToken(originToken); const derivativeToken = theme.getDerivativeToken(originToken);
// Merge with override // Merge with override
let mergedDerivativeToken = { let mergedDerivativeToken = {
...derivativeToken, ...derivativeToken,
@ -150,7 +153,6 @@ 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 => {

View File

@ -42,17 +42,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]
| Extract<CSSProperties[K], string>[] | readonly Extract<CSSProperties[K], string>[]
| { | {
[SKIP_CHECK]: boolean; [SKIP_CHECK]?: boolean;
[MULTI_VALUE]?: boolean; [MULTI_VALUE]?: boolean;
value: CSSProperties[K] | Extract<CSSProperties[K], string>[]; value: CSSProperties[K] | CSSProperties[K][];
}; };
}; };
export type CSSPseudos = { [K in CSS.Pseudos]?: CSSObject }; export type CSSPseudos = { [K in CSS.Pseudos]?: CSSObject };
type ArrayCSSInterpolation = CSSInterpolation[]; type ArrayCSSInterpolation = readonly CSSInterpolation[];
export type InterpolationPrimitive = null | undefined | boolean | number | string | CSSObject; export type InterpolationPrimitive = null | undefined | boolean | number | string | CSSObject;

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 ? 'pageYOffset' : 'pageXOffset']; result = target[top ? 'scrollY' : 'scrollX'];
} 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 = /* #__PURE__ */ isClient ? window : undefined; export const defaultWindow = isClient ? window : undefined;
export const defaultDocument = /* #__PURE__ */ isClient ? window.document : undefined; export const defaultDocument = isClient ? window.document : undefined;
export const defaultNavigator = /* #__PURE__ */ isClient ? window.navigator : undefined; export const defaultNavigator = isClient ? window.navigator : undefined;
export const defaultLocation = /* #__PURE__ */ isClient ? window.location : undefined; export const defaultLocation = isClient ? window.location : undefined;

View File

@ -21,8 +21,6 @@ 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 =
/* #__PURE__ */ isClient && isClient && window?.navigator?.userAgent && /iP(ad|hone|od)/.test(window.navigator.userAgent);
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

@ -1,24 +0,0 @@
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.pageXOffset, nextScrollTop); (container as Window).scrollTo(window.scrollX, nextScrollTop);
} else if (container instanceof Document || container.constructor.name === 'HTMLDocument') { } else if (container instanceof Document) {
(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,6 +1,7 @@
// 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', () => {
@ -26,6 +27,25 @@ 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, Transition, TransitionGroup } from 'vue'; import { nextTick } 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,6 +126,4 @@ const getTransitionName = (rootPrefixCls: string, motion: string, transitionName
return `${rootPrefixCls}-${motion}`; return `${rootPrefixCls}-${motion}`;
}; };
export { Transition, TransitionGroup, collapseMotion, getTransitionName, getTransitionDirection }; export { collapseMotion, getTransitionName, getTransitionDirection };
export default Transition;

View File

@ -1,8 +0,0 @@
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[] | JSX.Element; export type VueNode = VNodeChildAtom | VNodeChildAtom[] | VNode;
export const withInstall = <T>(comp: T) => { export const withInstall = <T>(comp: T) => {
const c = comp as any; const c = comp as any;

View File

@ -1,6 +1,6 @@
import { filterEmpty } from './props-util'; import { filterEmpty } from './props-util';
import type { VNode, VNodeProps } from 'vue'; import type { Slots, VNode, VNodeArrayChildren, VNodeProps } from 'vue';
import { cloneVNode, isVNode } from 'vue'; import { cloneVNode, isVNode, Comment, Fragment, render as VueRender } 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> &
@ -51,3 +51,32 @@ 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,6 +159,12 @@ 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

@ -33,13 +33,12 @@ export default defineComponent({
// =============================== Wave =============================== // =============================== Wave ===============================
const showWave = useWave( const showWave = useWave(
instance,
computed(() => classNames(prefixCls.value, hashId.value)), computed(() => classNames(prefixCls.value, hashId.value)),
wave, wave,
); );
let onClick: (e: MouseEvent) => void; let onClick: (e: MouseEvent) => void;
const clear = () => { const clear = () => {
const node = findDOMNode(instance); const node = findDOMNode(instance) as HTMLElement;
node.removeEventListener('click', onClick, true); node.removeEventListener('click', onClick, true);
}; };
onMounted(() => { onMounted(() => {

View File

@ -1,21 +1,25 @@
import type { ComponentInternalInstance, ComputedRef, Ref } from 'vue'; import type { ComputedRef, 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 }>, 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) { if (wave?.value?.disabled || !node) {
return; return;
} }
stopWave = showWaveEffect(node, className.value);
showWaveEffect(node, className.value);
} }
onBeforeUnmount(() => {
stopWave?.();
});
return showWave; return showWave;
} }

View File

@ -176,7 +176,6 @@ 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');
@ -256,7 +255,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 } = state; const { affixStyle, placeholderStyle, status } = state;
const className = classNames({ const className = classNames({
[prefixCls.value]: affixStyle, [prefixCls.value]: affixStyle,
[hashId.value]: true, [hashId.value]: true,
@ -271,7 +270,7 @@ const Affix = defineComponent({
]); ]);
return wrapSSR( return wrapSSR(
<ResizeObserver onResize={updatePosition}> <ResizeObserver onResize={updatePosition}>
<div {...restProps} {...attrs} ref={placeholderNode}> <div {...restProps} {...attrs} ref={placeholderNode} data-measure-status={status}>
{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 } from 'vue'; import { computed, defineComponent, shallowRef, Transition } 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, Transition } from '../_util/transition'; import { getTransitionProps } 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

@ -97,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 useGloablStore = defineStore('global', () => { export const useGlobalStore = 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

@ -98,7 +98,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 useGloablStore = defineStore('global', () => { export const useGlobalStore = 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,6 +50,8 @@ 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, Transition } from '../_util/transition'; import { getTransitionProps } from '../_util/transition';
import type { ExtractPropTypes, CSSProperties, PropType } from 'vue'; import type { ExtractPropTypes, CSSProperties, PropType } from 'vue';
import { defineComponent, computed, ref, watch } from 'vue'; import { defineComponent, computed, ref, watch, Transition } 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,6 +1,5 @@
import { defineComponent, nextTick } from 'vue'; import { defineComponent, nextTick, Transition } 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,7 +495,6 @@ 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,9 +16,11 @@ 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.
```html ```jsx
// The default locale is en-US, if you want to use other locale, just set locale in entry file // The default locale is en-US, if you want to use other locale, just set locale in entry file globally.
globally. // import dayjs from 'dayjs'; // import 'dayjs/locale/zh-cn'; // dayjs.locale('zh-cn'); // import dayjs from 'dayjs';
// 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,25 +17,27 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA
**注意:**Calendar 部分 locale 是从 value 中读取,所以请先正确设置 dayjs 的 locale。 **注意:**Calendar 部分 locale 是从 value 中读取,所以请先正确设置 dayjs 的 locale。
```html ```jsx
// 默认语言为 en-US所以如果需要使用其他语言推荐在入口文件全局设置 locale // import dayjs from // 默认语言为 en-US所以如果需要使用其他语言推荐在入口文件全局设置 locale
'dayjs'; // import 'dayjs/locale/zh-cn'; // dayjs.locale('zh-cn'); // import dayjs from 'dayjs';
// 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) | - | |
@ -43,8 +45,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, renderSlot } from 'vue'; import { isVNode, defineComponent } 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,6 +10,8 @@ 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;
@ -152,7 +154,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 = renderSlot(slots, 'customTab', item as any, () => [tab]); tab = customRenderSlot(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

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

View File

@ -2,8 +2,7 @@ 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 } from 'vue'; import { defineComponent, Transition } 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, 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](#components-collapse-demo-accordion), 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 | 默认无,accordion 模式下默认第一个元素 | | | activeKey(v-model) | 当前激活 tab 面板的 key | string\[] \| string <br> number\[] \| number | 默认无,[手风琴模式](#components-collapse-demo-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

@ -44,7 +44,7 @@ export default (name: string, props: Record<any, any>) => {
const csp = computed(() => props.csp ?? configProvider.csp); const csp = computed(() => props.csp ?? configProvider.csp);
const wave = computed<{ const wave = computed<{
disabled?: boolean; disabled?: boolean;
}>(() => props.wave ?? configProvider.wave.value); }>(() => props.wave ?? configProvider.wave?.value);
return { return {
configProvider, configProvider,

View File

@ -64,6 +64,7 @@ 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, Plugin, WatchStopHandle } from 'vue'; import type { App, MaybeRef, 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,7 +7,6 @@ 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';
@ -232,7 +231,6 @@ 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,6 +65,7 @@ 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` | |
| dropdownClassName | To customize the className of the popup calendar | string | - | | | popupClassName | 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` | |
| dropdownClassName | 额外的弹出日历 className | string | - | | | popupClassName | 额外的弹出日历 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,7 +6,15 @@ 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`]: {
borderInlineStart: lineWidth, borderInlineStartWidth: lineWidth,
borderInlineEnd: 0, borderInlineEnd: 0,
borderBlockStart: 0, borderBlockStart: 0,
borderBlockEnd: 0, borderBlockEnd: 0,

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 } from 'vue'; import { defineComponent, h } 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,9 +11,6 @@ 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;
} }
@ -40,13 +37,16 @@ const Empty = defineComponent({
return () => { return () => {
const prefixCls = prefixClsRef.value; const prefixCls = prefixClsRef.value;
const { const {
image = slots.image?.() || defaultEmptyImg, image: mergedImage = slots.image?.() || h(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`]: image === simpleEmptyImg, [`${prefixCls}-normal`]: isNormal,
[`${prefixCls}-rtl`]: direction.value === 'rtl', [`${prefixCls}-rtl`]: direction.value === 'rtl',
})} })}
{...restProps} {...restProps}
@ -85,7 +85,7 @@ const Empty = defineComponent({
}, },
}); });
Empty.PRESENTED_IMAGE_DEFAULT = defaultEmptyImg; Empty.PRESENTED_IMAGE_DEFAULT = () => h(DefaultEmptyImg);
Empty.PRESENTED_IMAGE_SIMPLE = simpleEmptyImg; Empty.PRESENTED_IMAGE_SIMPLE = () => h(SimpleEmptyImg);
export default withInstall(Empty); export default withInstall(Empty);

View File

@ -1,5 +1,5 @@
import VerticalAlignTopOutlined from '@ant-design/icons-vue/VerticalAlignTopOutlined'; import VerticalAlignTopOutlined from '@ant-design/icons-vue/VerticalAlignTopOutlined';
import { getTransitionProps, Transition } from '../_util/transition'; import { getTransitionProps } from '../_util/transition';
import { import {
defineComponent, defineComponent,
nextTick, nextTick,
@ -10,6 +10,7 @@ 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';

View File

@ -1,8 +1,8 @@
import { defineComponent, ref, computed, watch, onBeforeUnmount } from 'vue'; import { defineComponent, ref, computed, watch, onBeforeUnmount, Transition } 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, Transition } from '../_util/transition'; import { getTransitionProps } 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

@ -33,11 +33,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 | Set the handler to handle `click` event | `(event) => void` | - | | click | 设置处理 `click` 事件的处理器 | `(event) => void` | - |
### FloatButton.Group ### FloatButton.Group
@ -47,7 +47,7 @@ tag: New
| trigger | 触发方式(有触发方式为菜单模式) | `click` \| `hover` | - | | | trigger | 触发方式(有触发方式为菜单模式) | `click` \| `hover` | - | |
| open(v-model) | 受控展开 | boolean | - | | | open(v-model) | 受控展开 | boolean | - | |
### FloatButton.Group Events ### FloatButton.Group 事件
| 事件名称 | 说明 | 回调参数 | 版本 | | 事件名称 | 说明 | 回调参数 | 版本 |
| ---------- | ---------------- | ----------------------- | ---- | | ---------- | ---------------- | ----------------------- | ---- |

View File

@ -20,7 +20,7 @@ export const floatButtonProps = () => {
shape: stringType<FloatButtonShape>('circle'), shape: stringType<FloatButtonShape>('circle'),
tooltip: PropTypes.any, tooltip: PropTypes.any,
href: String, href: String,
target: functionType<() => Window | HTMLElement | null>(), target: String,
badge: objectType<FloatButtonBadgeProps>(), badge: objectType<FloatButtonBadgeProps>(),
onClick: functionType<MouseEventHandler>(), onClick: functionType<MouseEventHandler>(),
}; };

View File

@ -494,7 +494,7 @@ export default defineComponent({
> >
<Row <Row
{...attrs} {...attrs}
class={`${prefixCls.value}-row`} class={`${prefixCls.value}-item-row`}
key="row" key="row"
v-slots={{ v-slots={{
default: () => ( default: () => (

View File

@ -214,11 +214,13 @@ function useForm(
const errorList = results.filter( const errorList = results.filter(
(result: { errors: string | any[] }) => result && result.errors.length, (result: { errors: string | any[] }) => result && result.errors.length,
); );
return Promise.reject({ return errorList.length
values, ? Promise.reject({
errorFields: errorList, values,
outOfDate: lastValidatePromise !== summaryPromise, errorFields: errorList,
}); outOfDate: lastValidatePromise !== summaryPromise,
})
: Promise.resolve(values);
}); });
// Do not throw in console // Do not throw in console

View File

@ -50,6 +50,10 @@ const genGridRowStyle: GenerateStyle<GridRowToken> = (token): CSSObject => {
justifyContent: 'space-around', justifyContent: 'space-around',
}, },
'&-space-evenly ': {
justifyContent: 'space-evenly',
},
// Align at the top // Align at the top
'&-top': { '&-top': {
alignItems: 'flex-start', alignItems: 'flex-start',

View File

@ -122,6 +122,8 @@ See [iconfont.cn documents](http://iconfont.cn/help/detail?spm=a313x.7781069.199
### Custom SVG Icon ### Custom SVG Icon
#### vue cli 3
You can import SVG icon as an vue component by using `vue cli 3` and [`vue-svg-loader`](https://www.npmjs.com/package/vue-svg-loader). `vue-svg-loader`'s `options` [reference](https://github.com/visualfanatic/vue-svg-loader). You can import SVG icon as an vue component by using `vue cli 3` and [`vue-svg-loader`](https://www.npmjs.com/package/vue-svg-loader). `vue-svg-loader`'s `options` [reference](https://github.com/visualfanatic/vue-svg-loader).
```js ```js
@ -149,6 +151,84 @@ export default defineComponent({
}); });
``` ```
#### Rsbuild
Rsbuild is a new generation of build tool, official website https://rsbuild.dev/
Create your own `vue-svg-loader.js` file, which allows you to customize and beautify SVG, and then configure it in `rsbuild.config.ts`
```js
// vue-svg-loader.js
/* eslint-disable */
const { optimize } = require('svgo');
const { version } = require('vue');
const semverMajor = require('semver/functions/major');
module.exports = async function (svg) {
const callback = this.async();
try {
({ data: svg } = await optimize(svg, {
path: this.resourcePath,
js2svg: {
indent: 2,
pretty: true,
},
plugins: [
'convertStyleToAttrs',
'removeDoctype',
'removeXMLProcInst',
'removeComments',
'removeMetadata',
'removeTitle',
'removeDesc',
'removeStyleElement',
'removeXMLNS',
'removeXMLProcInst',
],
}));
} catch (error) {
callback(error);
return;
}
if (semverMajor(version) === 2) {
svg = svg.replace('<svg', '<svg v-on="$listeners"');
}
callback(null, `<template>${svg}</template>`);
};
```
```js
// rsbuild.config.ts
/* eslint-disable */
import { defineConfig } from '@rsbuild/core';
import { pluginVue } from '@rsbuild/plugin-vue';
export default defineConfig({
tools: {
bundlerChain(chain, { CHAIN_ID }) {
chain.module.rule(CHAIN_ID.RULE.SVG).exclude.add(/\.svg$/);
},
rspack: {
module: {
rules: [
{
test: /\.svg$/,
use: ['vue-loader', 'vue-svg-loader'],
},
],
},
resolveLoader: {
alias: {
'vue-svg-loader': require('path').join(__dirname, './vue-svg-loader.js'),
},
},
},
},
});
```
The following properties are available for the component: The following properties are available for the component:
| Property | Description | Type | Default | | Property | Description | Type | Default |

View File

@ -119,7 +119,9 @@ export default defineComponent({
### 自定义 SVG 图标 ### 自定义 SVG 图标
如果使用 `vue cli 3`,可以通过配置 [vue-svg-loader](https://www.npmjs.com/package/vue-svg-loader) 来将 `svg` 图标作为 `Vue` 组件导入。更多`vue-svg-loader` 的使用方式请参阅 [文档](https://github.com/visualfanatic/vue-svg-loader)。 #### vue cli 3
可以通过配置 [vue-svg-loader](https://www.npmjs.com/package/vue-svg-loader) 来将 `svg` 图标作为 `Vue` 组件导入。更多`vue-svg-loader` 的使用方式请参阅 [文档](https://github.com/visualfanatic/vue-svg-loader)。
```js ```js
// vue.config.js // vue.config.js
@ -146,6 +148,88 @@ export default defineComponent({
}); });
``` ```
#### Rsbuild
Rsbuild 是新一代构建工具,官网 https://rsbuild.dev/
自己实现一个 `vue-svg-loader.js` 文件,好处是可以自定义美化 svg然后在 `rsbuild.config.ts` 中配置:
```js
// vue-svg-loader.js
/* eslint-disable */
const { optimize } = require('svgo');
const { version } = require('vue');
const semverMajor = require('semver/functions/major');
module.exports = async function (svg) {
const callback = this.async();
try {
({ data: svg } = await optimize(svg, {
path: this.resourcePath,
js2svg: {
indent: 2,
pretty: true,
},
plugins: [
'convertStyleToAttrs',
'removeDoctype',
'removeXMLProcInst',
'removeComments',
'removeMetadata',
'removeTitle',
'removeDesc',
'removeStyleElement',
'removeXMLNS',
'removeXMLProcInst',
],
}));
} catch (error) {
callback(error);
return;
}
if (semverMajor(version) === 2) {
svg = svg.replace('<svg', '<svg v-on="$listeners"');
}
callback(null, `<template>${svg}</template>`);
};
```
```js
// rsbuild.config.ts
/* eslint-disable */
import { defineConfig } from '@rsbuild/core';
import { pluginVue } from '@rsbuild/plugin-vue';
export default defineConfig({
tools: {
bundlerChain(chain, { CHAIN_ID }) {
chain.module
// 先给svg排除默认的规则方便下面自定义loader
.rule(CHAIN_ID.RULE.SVG)
.exclude.add(/\.svg$/);
},
rspack: {
module: {
rules: [
{
test: /\.svg$/,
use: ['vue-loader', 'vue-svg-loader'],
},
],
},
resolveLoader: {
alias: {
'vue-svg-loader': require('path').join(__dirname, './vue-svg-loader.js'),
},
},
},
},
});
```
`Icon` 中的 `component` 组件的接受的属性如下: `Icon` 中的 `component` 组件的接受的属性如下:
| 字段 | 说明 | 类型 | 只读值 | | 字段 | 说明 | 类型 | 只读值 |

View File

@ -13,10 +13,10 @@ export type ImageProps = Partial<
ExtractPropTypes<ReturnType<typeof imageProps>> & ExtractPropTypes<ReturnType<typeof imageProps>> &
Omit<ImgHTMLAttributes, 'placeholder' | 'onClick'> Omit<ImgHTMLAttributes, 'placeholder' | 'onClick'>
>; >;
const Image = defineComponent<ImageProps>({ const Image = defineComponent({
name: 'AImage', name: 'AImage',
inheritAttrs: false, inheritAttrs: false,
props: imageProps() as any, props: imageProps(),
setup(props, { slots, attrs }) { setup(props, { slots, attrs }) {
const { prefixCls, rootPrefixCls, configProvider } = useConfigInject('image', props); const { prefixCls, rootPrefixCls, configProvider } = useConfigInject('image', props);
// Style // Style

View File

@ -72,7 +72,7 @@ const InputNumber = defineComponent({
const mergedSize = computed(() => compactSize.value || size.value); const mergedSize = computed(() => compactSize.value || size.value);
const mergedValue = shallowRef(props.value === undefined ? props.defaultValue : props.value); const mergedValue = shallowRef(props.value ?? props.defaultValue);
const focused = shallowRef(false); const focused = shallowRef(false);
watch( watch(
() => props.value, () => props.value,

View File

@ -34,7 +34,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*1uH-R5kLAMIAAA
| parser | 指定从 formatter 里转换回数字的方式,和 formatter 搭配使用 | function( string): number | - | | | parser | 指定从 formatter 里转换回数字的方式,和 formatter 搭配使用 | function( string): number | - | |
| precision | 数值精度 | number | - | | | precision | 数值精度 | number | - | |
| prefix | 带有前缀图标的 input | slot | - | 3.0 | | prefix | 带有前缀图标的 input | slot | - | 3.0 |
| size | 输入框大小 | string | | | | size | 输入框大小 | string | - | |
| status | 设置校验状态 | 'error' \| 'warning' | - | 3.3.0 | | status | 设置校验状态 | 'error' \| 'warning' | - | 3.3.0 |
| step | 每次改变步数,可以为小数 | number\|string | 1 | | | step | 每次改变步数,可以为小数 | number\|string | 1 | |
| stringMode | 字符值模式,开启后支持高精度小数。同时 `change` 事件将返回 string 类型 | boolean | false | 3.0 | | stringMode | 字符值模式,开启后支持高精度小数。同时 `change` 事件将返回 string 类型 | boolean | false | 3.0 |

View File

@ -395,6 +395,11 @@ export default defineComponent({
} }
}; };
// Solve the issue of the event triggering sequence when entering numbers in chinese input (Safari)
const onBeforeInput = () => {
userTypingRef.value = true;
};
const onKeyDown: KeyboardEventHandler = event => { const onKeyDown: KeyboardEventHandler = event => {
const { which } = event; const { which } = event;
userTypingRef.value = true; userTypingRef.value = true;
@ -577,6 +582,7 @@ export default defineComponent({
onBlur={onBlur} onBlur={onBlur}
onCompositionstart={onCompositionStart} onCompositionstart={onCompositionStart}
onCompositionend={onCompositionEnd} onCompositionend={onCompositionEnd}
onBeforeinput={onBeforeInput}
/> />
</div> </div>
</div> </div>

View File

@ -263,6 +263,10 @@ const genInputNumberStyles: GenerateStyle<InputNumberToken> = (token: InputNumbe
[`${componentCls}-handler-wrap`]: { [`${componentCls}-handler-wrap`]: {
display: 'none', display: 'none',
}, },
[`${componentCls}-input`]: {
color: 'inherit',
},
}, },
[` [`

View File

@ -59,7 +59,7 @@ export default defineComponent({
}); });
const getIcon = (prefixCls: string) => { const getIcon = (prefixCls: string) => {
const { action, iconRender = slots.iconRender || defaultIconRender } = props; const { action, iconRender = slots.iconRender || defaultIconRender } = props;
const iconTrigger = ActionMap[action!] || ''; const iconTrigger = ActionMap[action] || '';
const icon = iconRender(visible.value); const icon = iconRender(visible.value);
const iconProps = { const iconProps = {
[iconTrigger]: onVisibleChange, [iconTrigger]: onVisibleChange,

View File

@ -1,26 +1,26 @@
import type { VNode } from 'vue'; import type { CSSProperties } from 'vue';
import { import {
onMounted, computed,
watchEffect,
getCurrentInstance, getCurrentInstance,
watch, watch,
onBeforeUnmount, onBeforeUnmount,
ref, ref,
nextTick,
defineComponent, defineComponent,
withDirectives,
} from 'vue'; } from 'vue';
import ResizeObserver from '../vc-resize-observer'; import ResizeObserver from '../vc-resize-observer';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import calculateNodeHeight from './calculateNodeHeight';
import raf from '../_util/raf'; import raf from '../_util/raf';
import warning from '../_util/warning'; import warning from '../_util/warning';
import antInput from '../_util/antInputDirective';
import omit from '../_util/omit'; import omit from '../_util/omit';
import { textAreaProps } from './inputProps'; import { textAreaProps } from './inputProps';
import calculateAutoSizeStyle from './calculateNodeHeight';
import type { BaseInputExpose } from '../_util/BaseInput';
import BaseInput from '../_util/BaseInput';
const RESIZE_STATUS_NONE = 0; const RESIZE_START = 0;
const RESIZE_STATUS_RESIZING = 1; const RESIZE_MEASURING = 1;
const RESIZE_STATUS_RESIZED = 2; const RESIZE_STABLE = 2;
const ResizableTextArea = defineComponent({ const ResizableTextArea = defineComponent({
compatConfig: { MODE: 3 }, compatConfig: { MODE: 3 },
@ -30,9 +30,9 @@ const ResizableTextArea = defineComponent({
setup(props, { attrs, emit, expose }) { setup(props, { attrs, emit, expose }) {
let nextFrameActionId: any; let nextFrameActionId: any;
let resizeFrameId: any; let resizeFrameId: any;
const textAreaRef = ref(); const textAreaRef = ref<BaseInputExpose>();
const textareaStyles = ref({}); const textareaStyles = ref({});
const resizeStatus = ref(RESIZE_STATUS_NONE); const resizeStatus = ref(RESIZE_STABLE);
onBeforeUnmount(() => { onBeforeUnmount(() => {
raf.cancel(nextFrameActionId); raf.cancel(nextFrameActionId);
raf.cancel(resizeFrameId); raf.cancel(resizeFrameId);
@ -41,10 +41,12 @@ const ResizableTextArea = defineComponent({
// https://github.com/ant-design/ant-design/issues/21870 // https://github.com/ant-design/ant-design/issues/21870
const fixFirefoxAutoScroll = () => { const fixFirefoxAutoScroll = () => {
try { try {
if (document.activeElement === textAreaRef.value) { if (textAreaRef.value && document.activeElement === textAreaRef.value.input) {
const currentStart = textAreaRef.value.selectionStart; const currentStart = textAreaRef.value.getSelectionStart();
const currentEnd = textAreaRef.value.selectionEnd; const currentEnd = textAreaRef.value.getSelectionEnd();
const scrollTop = textAreaRef.value.getScrollTop();
textAreaRef.value.setSelectionRange(currentStart, currentEnd); textAreaRef.value.setSelectionRange(currentStart, currentEnd);
textAreaRef.value.setScrollTop(scrollTop);
} }
} catch (e) { } catch (e) {
// Fix error in Chrome: // Fix error in Chrome:
@ -52,41 +54,82 @@ const ResizableTextArea = defineComponent({
// http://stackoverflow.com/q/21177489/3040605 // http://stackoverflow.com/q/21177489/3040605
} }
}; };
const minRows = ref<number>();
const resizeTextarea = () => { const maxRows = ref<number>();
const autoSize = props.autoSize || props.autosize; watchEffect(() => {
if (!autoSize || !textAreaRef.value) {
return;
}
const { minRows, maxRows } = autoSize;
textareaStyles.value = calculateNodeHeight(textAreaRef.value, false, minRows, maxRows);
resizeStatus.value = RESIZE_STATUS_RESIZING;
raf.cancel(resizeFrameId);
resizeFrameId = raf(() => {
resizeStatus.value = RESIZE_STATUS_RESIZED;
resizeFrameId = raf(() => {
resizeStatus.value = RESIZE_STATUS_NONE;
fixFirefoxAutoScroll();
});
});
};
const resizeOnNextFrame = () => {
raf.cancel(nextFrameActionId);
nextFrameActionId = raf(resizeTextarea);
};
const handleResize = (size: { width: number; height: number }) => {
if (resizeStatus.value !== RESIZE_STATUS_NONE) {
return;
}
emit('resize', size);
const autoSize = props.autoSize || props.autosize; const autoSize = props.autoSize || props.autosize;
if (autoSize) { if (autoSize) {
resizeOnNextFrame(); minRows.value = autoSize.minRows;
maxRows.value = autoSize.maxRows;
} else {
minRows.value = undefined;
maxRows.value = undefined;
}
});
const needAutoSize = computed(() => !!(props.autoSize || props.autosize));
const startResize = () => {
resizeStatus.value = RESIZE_START;
};
watch(
[() => props.value, minRows, maxRows, needAutoSize],
() => {
if (needAutoSize.value) {
startResize();
}
},
{ immediate: true },
);
const autoSizeStyle = ref<CSSProperties>();
watch(
[resizeStatus, textAreaRef],
() => {
if (!textAreaRef.value) return;
if (resizeStatus.value === RESIZE_START) {
resizeStatus.value = RESIZE_MEASURING;
} else if (resizeStatus.value === RESIZE_MEASURING) {
const textareaStyles = calculateAutoSizeStyle(
textAreaRef.value.input as HTMLTextAreaElement,
false,
minRows.value,
maxRows.value,
);
resizeStatus.value = RESIZE_STABLE;
autoSizeStyle.value = textareaStyles;
} else {
fixFirefoxAutoScroll();
}
},
{ immediate: true, flush: 'post' },
);
const instance = getCurrentInstance();
const resizeRafRef = ref();
const cleanRaf = () => {
raf.cancel(resizeRafRef.value);
};
const onInternalResize = (size: { width: number; height: number }) => {
if (resizeStatus.value === RESIZE_STABLE) {
emit('resize', size);
if (needAutoSize.value) {
cleanRaf();
resizeRafRef.value = raf(() => {
startResize();
});
}
} }
}; };
onBeforeUnmount(() => {
cleanRaf();
});
const resizeTextarea = () => {
startResize();
};
expose({
resizeTextarea,
textArea: computed(() => textAreaRef.value?.input),
instance,
});
warning( warning(
props.autosize === undefined, props.autosize === undefined,
'Input.TextArea', 'Input.TextArea',
@ -94,7 +137,7 @@ const ResizableTextArea = defineComponent({
); );
const renderTextArea = () => { const renderTextArea = () => {
const { prefixCls, autoSize, autosize, disabled } = props; const { prefixCls, disabled } = props;
const otherProps = omit(props, [ const otherProps = omit(props, [
'prefixCls', 'prefixCls',
'onPressEnter', 'onPressEnter',
@ -103,26 +146,26 @@ const ResizableTextArea = defineComponent({
'defaultValue', 'defaultValue',
'allowClear', 'allowClear',
'type', 'type',
'lazy',
'maxlength', 'maxlength',
'valueModifiers', 'valueModifiers',
]); ]);
const cls = classNames(prefixCls, attrs.class, { const cls = classNames(prefixCls, attrs.class, {
[`${prefixCls}-disabled`]: disabled, [`${prefixCls}-disabled`]: disabled,
}); });
const style = [ const mergedAutoSizeStyle = needAutoSize.value ? autoSizeStyle.value : null;
attrs.style, const style = [attrs.style, textareaStyles.value, mergedAutoSizeStyle];
textareaStyles.value,
resizeStatus.value === RESIZE_STATUS_RESIZING
? { overflowX: 'hidden', overflowY: 'hidden' }
: null,
];
const textareaProps: any = { const textareaProps: any = {
...otherProps, ...otherProps,
...attrs, ...attrs,
style, style,
class: cls, class: cls,
}; };
if (resizeStatus.value === RESIZE_START || resizeStatus.value === RESIZE_MEASURING) {
style.push({
overflowX: 'hidden',
overflowY: 'hidden',
});
}
if (!textareaProps.autofocus) { if (!textareaProps.autofocus) {
delete textareaProps.autofocus; delete textareaProps.autofocus;
} }
@ -130,34 +173,12 @@ const ResizableTextArea = defineComponent({
delete textareaProps.rows; delete textareaProps.rows;
} }
return ( return (
<ResizeObserver onResize={handleResize} disabled={!(autoSize || autosize)}> <ResizeObserver onResize={onInternalResize} disabled={!needAutoSize.value}>
{withDirectives((<textarea {...textareaProps} ref={textAreaRef} />) as VNode, [ <BaseInput {...textareaProps} ref={textAreaRef} tag="textarea"></BaseInput>
[antInput],
])}
</ResizeObserver> </ResizeObserver>
); );
}; };
watch(
() => props.value,
() => {
nextTick(() => {
resizeTextarea();
});
},
);
onMounted(() => {
nextTick(() => {
resizeTextarea();
});
});
const instance = getCurrentInstance();
expose({
resizeTextarea,
textArea: textAreaRef,
instance,
});
return () => { return () => {
return renderTextArea(); return renderTextArea();
}; };

View File

@ -38,10 +38,10 @@ function setTriggerValue(
let newTriggerValue = triggerValue; let newTriggerValue = triggerValue;
if (isCursorInEnd) { if (isCursorInEnd) {
// //
newTriggerValue = fixEmojiLength(triggerValue, maxLength!); newTriggerValue = fixEmojiLength(triggerValue, maxLength);
} else if ( } else if (
[...(preValue || '')].length < triggerValue.length && [...(preValue || '')].length < triggerValue.length &&
[...(triggerValue || '')].length > maxLength! [...(triggerValue || '')].length > maxLength
) { ) {
// //
newTriggerValue = preValue; newTriggerValue = preValue;
@ -58,7 +58,7 @@ export default defineComponent({
const formItemContext = useInjectFormItemContext(); const formItemContext = useInjectFormItemContext();
const formItemInputContext = FormItemInputContext.useInject(); const formItemInputContext = FormItemInputContext.useInject();
const mergedStatus = computed(() => getMergedStatus(formItemInputContext.status, props.status)); const mergedStatus = computed(() => getMergedStatus(formItemInputContext.status, props.status));
const stateValue = shallowRef(props.value === undefined ? props.defaultValue : props.value); const stateValue = shallowRef(props.value ?? props.defaultValue);
const resizableTextArea = shallowRef(); const resizableTextArea = shallowRef();
const mergedValue = shallowRef(''); const mergedValue = shallowRef('');
const { prefixCls, size, direction } = useConfigInject('input', props); const { prefixCls, size, direction } = useConfigInject('input', props);
@ -79,7 +79,7 @@ export default defineComponent({
const onInternalCompositionStart = (e: CompositionEvent) => { const onInternalCompositionStart = (e: CompositionEvent) => {
compositing.value = true; compositing.value = true;
// //
oldCompositionValueRef.value = mergedValue.value as string; oldCompositionValueRef.value = mergedValue.value;
// //
oldSelectionStartRef.value = (e.currentTarget as any).selectionStart; oldSelectionStartRef.value = (e.currentTarget as any).selectionStart;
emit('compositionstart', e); emit('compositionstart', e);
@ -94,7 +94,7 @@ export default defineComponent({
oldSelectionStartRef.value === oldCompositionValueRef.value?.length; oldSelectionStartRef.value === oldCompositionValueRef.value?.length;
triggerValue = setTriggerValue( triggerValue = setTriggerValue(
isCursorInEnd, isCursorInEnd,
oldCompositionValueRef.value as string, oldCompositionValueRef.value,
triggerValue, triggerValue,
props.maxlength, props.maxlength,
); );
@ -170,23 +170,21 @@ export default defineComponent({
}; };
const handleChange = (e: Event) => { const handleChange = (e: Event) => {
const { composing } = e.target as any;
let triggerValue = (e.target as any).value; let triggerValue = (e.target as any).value;
compositing.value = !!((e as any).isComposing || composing); if (stateValue.value === triggerValue) return;
if ((compositing.value && props.lazy) || stateValue.value === triggerValue) return;
if (hasMaxLength.value) { if (hasMaxLength.value) {
// 1. maxlength 2.maxlength // 1. maxlength 2.maxlength
const target = e.target as any; const target = e.target as any;
const isCursorInEnd = const isCursorInEnd =
target.selectionStart >= props.maxlength! + 1 || target.selectionStart >= props.maxlength + 1 ||
target.selectionStart === triggerValue.length || target.selectionStart === triggerValue.length ||
!target.selectionStart; !target.selectionStart;
triggerValue = setTriggerValue( triggerValue = setTriggerValue(
isCursorInEnd, isCursorInEnd,
mergedValue.value as string, mergedValue.value,
triggerValue, triggerValue,
props.maxlength!, props.maxlength,
); );
} }
resolveOnChange(e.currentTarget as any, e, triggerChange, triggerValue); resolveOnChange(e.currentTarget as any, e, triggerChange, triggerValue);
@ -227,6 +225,7 @@ export default defineComponent({
id={resizeProps?.id ?? formItemContext.id.value} id={resizeProps?.id ?? formItemContext.id.value}
ref={resizableTextArea} ref={resizableTextArea}
maxlength={props.maxlength} maxlength={props.maxlength}
lazy={props.lazy}
/> />
); );
}; };
@ -238,7 +237,7 @@ export default defineComponent({
}); });
watchEffect(() => { watchEffect(() => {
let val = fixControlledValue(stateValue.value) as string; let val = fixControlledValue(stateValue.value);
if ( if (
!compositing.value && !compositing.value &&
hasMaxLength.value && hasMaxLength.value &&

View File

@ -1,21 +1,19 @@
// Thanks to https://github.com/andreypopp/react-textarea-autosize/
import type { CSSProperties } from 'vue'; import type { CSSProperties } from 'vue';
/** /**
* calculateNodeHeight(uiTextNode, useCache = false) * calculateNodeHeight(uiTextNode, useCache = false)
*/ */
const HIDDEN_TEXTAREA_STYLE = ` const HIDDEN_TEXTAREA_STYLE = `
min-height:0 !important; min-height:0 !important;
max-height:none !important; max-height:none !important;
height:0 !important; height:0 !important;
visibility:hidden !important; visibility:hidden !important;
overflow:hidden !important; overflow:hidden !important;
position:absolute !important; position:absolute !important;
z-index:-1000 !important; z-index:-1000 !important;
top:0 !important; top:0 !important;
right:0 !important right:0 !important;
pointer-events: none !important;
`; `;
const SIZING_STYLE = [ const SIZING_STYLE = [
@ -36,6 +34,7 @@ const SIZING_STYLE = [
'border-width', 'border-width',
'box-sizing', 'box-sizing',
'word-break', 'word-break',
'white-space',
]; ];
export interface NodeType { export interface NodeType {
@ -45,13 +44,12 @@ export interface NodeType {
boxSizing: string; boxSizing: string;
} }
const computedStyleCache: { [key: string]: NodeType } = {}; const computedStyleCache: Record<string, NodeType> = {};
let hiddenTextarea: HTMLTextAreaElement; let hiddenTextarea: HTMLTextAreaElement;
export function calculateNodeStyling(node: HTMLElement, useCache = false) { export function calculateNodeStyling(node: HTMLElement, useCache = false) {
const nodeRef = (node.getAttribute('id') || const nodeRef =
node.getAttribute('data-reactid') || node.getAttribute('id') || node.getAttribute('data-reactid') || node.getAttribute('name');
node.getAttribute('name')) as string;
if (useCache && computedStyleCache[nodeRef]) { if (useCache && computedStyleCache[nodeRef]) {
return computedStyleCache[nodeRef]; return computedStyleCache[nodeRef];
@ -88,7 +86,7 @@ export function calculateNodeStyling(node: HTMLElement, useCache = false) {
return nodeInfo; return nodeInfo;
} }
export default function calculateNodeHeight( export default function calculateAutoSizeStyle(
uiTextNode: HTMLTextAreaElement, uiTextNode: HTMLTextAreaElement,
useCache = false, useCache = false,
minRows: number | null = null, minRows: number | null = null,
@ -104,7 +102,7 @@ export default function calculateNodeHeight(
// Fix wrap="off" issue // Fix wrap="off" issue
// https://github.com/ant-design/ant-design/issues/6577 // https://github.com/ant-design/ant-design/issues/6577
if (uiTextNode.getAttribute('wrap')) { if (uiTextNode.getAttribute('wrap')) {
hiddenTextarea.setAttribute('wrap', uiTextNode.getAttribute('wrap') as string); hiddenTextarea.setAttribute('wrap', uiTextNode.getAttribute('wrap'));
} else { } else {
hiddenTextarea.removeAttribute('wrap'); hiddenTextarea.removeAttribute('wrap');
} }
@ -122,11 +120,12 @@ export default function calculateNodeHeight(
hiddenTextarea.setAttribute('style', `${sizingStyle};${HIDDEN_TEXTAREA_STYLE}`); hiddenTextarea.setAttribute('style', `${sizingStyle};${HIDDEN_TEXTAREA_STYLE}`);
hiddenTextarea.value = uiTextNode.value || uiTextNode.placeholder || ''; hiddenTextarea.value = uiTextNode.value || uiTextNode.placeholder || '';
let minHeight = Number.MIN_SAFE_INTEGER; let minHeight: number | undefined = undefined;
let maxHeight = Number.MAX_SAFE_INTEGER; let maxHeight: number | undefined = undefined;
let height = hiddenTextarea.scrollHeight;
let overflowY: any; let overflowY: any;
let height = hiddenTextarea.scrollHeight;
if (boxSizing === 'border-box') { if (boxSizing === 'border-box') {
// border-box: add border, since height = content + padding + border // border-box: add border, since height = content + padding + border
height += borderSize; height += borderSize;
@ -155,11 +154,19 @@ export default function calculateNodeHeight(
height = Math.min(maxHeight, height); height = Math.min(maxHeight, height);
} }
} }
return {
const style: CSSProperties = {
height: `${height}px`, height: `${height}px`,
minHeight: `${minHeight}px`,
maxHeight: `${maxHeight}px`,
overflowY, overflowY,
resize: 'none', resize: 'none',
}; };
if (minHeight) {
style.minHeight = `${minHeight}px`;
}
if (maxHeight) {
style.maxHeight = `${maxHeight}px`;
}
return style;
} }

View File

@ -29,7 +29,7 @@ A basic widget for getting the user input is a text field. Keyboard and mouse ca
| id | The ID for input | string | | | | id | The ID for input | string | | |
| maxlength | max length | number | | 1.5.0 | | maxlength | max length | number | | 1.5.0 |
| prefix | The prefix icon for the Input. | string\|slot | | | | prefix | The prefix icon for the Input. | string\|slot | | |
| showCount | Whether show text count | boolean | false | 3.0 | | showCount | Whether show text count | boolean \| { formatter: (info: { value: string, count: number, maxLength?: number }) => string | false | 3.0 |
| status | Set validation status | 'error' \| 'warning' | - | 3.3.0 | | status | Set validation status | 'error' \| 'warning' | - | 3.3.0 |
| size | The size of the input box. Note: in the context of a form, the `middle` size is used. Available: `large` `middle` `small` | string | - | | | size | The size of the input box. Note: in the context of a form, the `middle` size is used. Available: `large` `middle` `small` | string | - | |
| suffix | The suffix icon for the Input. | string\|slot | | | | suffix | The suffix icon for the Input. | string\|slot | | |
@ -52,7 +52,7 @@ A basic widget for getting the user input is a text field. Keyboard and mouse ca
| allowClear | allow to remove input content with clear icon | boolean | | 1.5.0 | | | allowClear | allow to remove input content with clear icon | boolean | | 1.5.0 | |
| autosize | Height autosize feature, can be set to `true | false`or an object`{ minRows: 2, maxRows: 6 }` | boolean\|object | false | | | autosize | Height autosize feature, can be set to `true | false`or an object`{ minRows: 2, maxRows: 6 }` | boolean\|object | false | |
| defaultValue | The initial input content | string | | | | | defaultValue | The initial input content | string | | | |
| showCount | Whether show text count | boolean | false | | | | showCount | Whether show text count | boolean \| { formatter: (info: { value: string, count: number, maxLength?: number }) => string | false | | |
| value(v-model) | The input content value | string | | | | | value(v-model) | The input content value | string | | | |
### TextArea Events ### TextArea Events

View File

@ -30,7 +30,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*sBqqTatJ-AkAAA
| id | 输入框的 id | string | | | | id | 输入框的 id | string | | |
| maxlength | 最大长度 | number | | 1.5.0 | | maxlength | 最大长度 | number | | 1.5.0 |
| prefix | 带有前缀图标的 input | string\|slot | | | | prefix | 带有前缀图标的 input | string\|slot | | |
| showCount | 是否展示字数 | boolean | false | 3.0 | | showCount | 是否展示字数 | boolean \| { formatter: (info: { value: string, count: number, maxLength?: number }) => string } | false | 3.0 |
| status | 设置校验状态 | 'error' \| 'warning' | - | 3.3.0 | | status | 设置校验状态 | 'error' \| 'warning' | - | 3.3.0 |
| size | 控件大小。注:标准表单内的输入框大小限制为 `middle`。可选 `large` `middle` `small` | string | - | | | size | 控件大小。注:标准表单内的输入框大小限制为 `middle`。可选 `large` `middle` `small` | string | - | |
| suffix | 带有后缀图标的 input | string\|slot | | | | suffix | 带有后缀图标的 input | string\|slot | | |
@ -53,7 +53,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*sBqqTatJ-AkAAA
| allowClear | 可以点击清除图标删除内容 | boolean | | 1.5.0 | | | allowClear | 可以点击清除图标删除内容 | boolean | | 1.5.0 | |
| autosize | 自适应内容高度,可设置为 `true | false` 或对象:`{ minRows: 2, maxRows: 6 }` | boolean\|object | false | | | autosize | 自适应内容高度,可设置为 `true | false` 或对象:`{ minRows: 2, maxRows: 6 }` | boolean\|object | false | |
| defaultValue | 输入框默认内容 | string | | | | | defaultValue | 输入框默认内容 | string | | | |
| showCount | 是否展示字数 | boolean | false | | | _| showCount | 是否展示字数 | boolean \| { formatter: (info: { value: string, count: number, maxLength?: number }) => string } | false | | |_
| value(v-model) | 输入框内容 | string | | | | | value(v-model) | 输入框内容 | string | | | |
### TextArea 事件 ### TextArea 事件

View File

@ -45,6 +45,7 @@ const genLayoutStyle: GenerateStyle<LayoutToken, CSSObject> = token => {
display: 'flex', display: 'flex',
flex: 'auto', flex: 'auto',
flexDirection: 'column', flexDirection: 'column',
color: colorText,
/* fix firefox can't set height smaller than content on flex item */ /* fix firefox can't set height smaller than content on flex item */
minHeight: 0, minHeight: 0,

View File

@ -139,6 +139,7 @@ const localeValues: Locale = {
QRCode: { QRCode: {
expired: 'QR code expired', expired: 'QR code expired',
refresh: 'Refresh', refresh: 'Refresh',
scanned: 'Scanned',
}, },
}; };

View File

@ -48,6 +48,7 @@ export interface Locale {
QRCode?: { QRCode?: {
expired?: string; expired?: string;
refresh?: string; refresh?: string;
scanned?: string;
}; };
} }

View File

@ -34,6 +34,11 @@ const localeValues: Locale = {
triggerAsc: 'Нажмите для сортировки по возрастанию', triggerAsc: 'Нажмите для сортировки по возрастанию',
cancelSort: 'Нажмите, чтобы отменить сортировку', cancelSort: 'Нажмите, чтобы отменить сортировку',
}, },
Tour: {
Next: 'Вперёд',
Previous: 'Назад',
Finish: 'Готово',
},
Modal: { Modal: {
okText: 'OK', okText: 'OK',
cancelText: 'Отмена', cancelText: 'Отмена',
@ -78,6 +83,7 @@ const localeValues: Locale = {
back: 'Назад', back: 'Назад',
}, },
Form: { Form: {
optional: '(опционально)',
defaultValidateMessages: { defaultValidateMessages: {
default: 'Ошибка проверки поля ${label}', default: 'Ошибка проверки поля ${label}',
required: 'Пожалуйста, введите ${label}', required: 'Пожалуйста, введите ${label}',
@ -128,6 +134,11 @@ const localeValues: Locale = {
Image: { Image: {
preview: 'Предпросмотр', preview: 'Предпросмотр',
}, },
QRCode: {
expired: 'QR код просрочен',
refresh: 'Обновить',
scanned: 'Отсканирован',
},
}; };
export default localeValues; export default localeValues;

View File

@ -138,6 +138,7 @@ const localeValues: Locale = {
QRCode: { QRCode: {
expired: '二维码已过期', expired: '二维码已过期',
refresh: '点击刷新', refresh: '点击刷新',
scanned: '已扫描',
}, },
}; };

View File

@ -131,6 +131,11 @@ const localeValues: Locale = {
Image: { Image: {
preview: '預覽', preview: '預覽',
}, },
QRCode: {
expired: '二維碼過期',
refresh: '點擊刷新',
scanned: '已掃描',
},
}; };
export default localeValues; export default localeValues;

View File

@ -132,6 +132,11 @@ const localeValues: Locale = {
Image: { Image: {
preview: '預覽', preview: '預覽',
}, },
QRCode: {
expired: '二維碼過期',
refresh: '點擊刷新',
scanned: '已掃描',
},
}; };
export default localeValues; export default localeValues;

View File

@ -3,7 +3,7 @@
order: 4 order: 4
title: title:
zh-CN: 向上展开 zh-CN: 向上展开
en-US: Placemen en-US: Placement
--- ---
## zh-CN ## zh-CN

View File

@ -30,7 +30,7 @@ More layouts with navigation: [Layout](/components/layout).
| forceSubMenuRender | render submenu into DOM before it shows | boolean | false | | forceSubMenuRender | render submenu into DOM before it shows | boolean | false |
| inlineCollapsed | specifies the collapsed status when menu is inline mode | boolean | - | | inlineCollapsed | specifies the collapsed status when menu is inline mode | boolean | - |
| inlineIndent | indent px of inline menu item on each level | number | 24 | | inlineIndent | indent px of inline menu item on each level | number | 24 |
| items | Menu item content | [ItemType\[\]](#ItemType) | - | 4.20.0 | | items | Menu item content | [ItemType\[\]](#itemtype) | - | 4.20.0 |
| mode | type of the menu; `vertical`, `horizontal`, and `inline` modes are supported | `vertical` \| `horizontal` \| `inline` | `vertical` | | mode | type of the menu; `vertical`, `horizontal`, and `inline` modes are supported | `vertical` \| `horizontal` \| `inline` | `vertical` |
| multiple | Allow selection of multiple items | boolean | false | | multiple | Allow selection of multiple items | boolean | false |
| openKeys(v-model) | array with the keys of currently opened sub menus | (string \| number)[] | | | openKeys(v-model) | array with the keys of currently opened sub menus | (string \| number)[] | |
@ -54,15 +54,15 @@ More layouts with navigation: [Layout](/components/layout).
### Menu.Item ### Menu.Item
| Param | Description | Type | Default value | | Param | Description | Type | Default value |
| -------- | ------------------------------------ | -------------- | ------------- | | -------- | ------------------------------------ | ---------------- | ------------- |
| disabled | whether menu item is disabled or not | boolean | false | | disabled | whether menu item is disabled or not | boolean | false |
| key | unique id of the menu item | string \| number | | | key | unique id of the menu item | string \| number | |
| title | set display title for collapsed item | string \| slot | | | title | set display title for collapsed item | string \| slot | |
### ItemType ### ItemType
> type ItemType = [MenuItemType](#MenuItemType) | [SubMenuType](#SubMenuType) | [MenuItemGroupType](#MenuItemGroupType) | [MenuDividerType](#MenuDividerType); > type ItemType = [MenuItemType](#menuitemtype) | [SubMenuType](#submenutype) | [MenuItemGroupType](#menuitemgrouptype) | [MenuDividerType](#menudividertype);
#### MenuItemType #### MenuItemType
@ -80,7 +80,7 @@ More layouts with navigation: [Layout](/components/layout).
<!-- prettier-ignore --> <!-- prettier-ignore -->
| Property | Description | Type | Default value | Version | | Property | Description | Type | Default value | Version |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| children | Sub-menus or sub-menu items | [ItemType\[\]](#ItemType) | - | | | children | Sub-menus or sub-menu items | [ItemType\[\]](#itemtype) | - | |
| disabled | Whether sub-menu is disabled | boolean | false | | | disabled | Whether sub-menu is disabled | boolean | false | |
| icon | Icon of sub menu | VueNode \| (item: SubMenuType) => VueNode | - | | | icon | Icon of sub menu | VueNode \| (item: SubMenuType) => VueNode | - | |
| key | Unique ID of the sub-menu | string \| number | - | | | key | Unique ID of the sub-menu | string \| number | - | |
@ -104,8 +104,8 @@ const groupItem = {
| Param | Description | Type | Default value | Version | | Param | Description | Type | Default value | Version |
| -------- | ---------------------- | --------------------------------- | ------------- | ------- | | -------- | ---------------------- | --------------------------------- | ------------- | ------- |
| children | Sub-menu items | [MenuItemType\[\]](#MenuItemType) | - | | | children | Sub-menu items | [MenuItemType\[\]](#menuitemtype) | - | |
| label | The title of the group | VueNode | - | | | label | The title of the group | VueNode | - | |
#### MenuDividerType #### MenuDividerType

View File

@ -31,7 +31,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*Vn4XSqJFAxcAAA
| forceSubMenuRender | 在子菜单展示之前就渲染进 DOM | boolean | false | | forceSubMenuRender | 在子菜单展示之前就渲染进 DOM | boolean | false |
| inlineCollapsed | inline 时菜单是否收起状态 | boolean | - | | inlineCollapsed | inline 时菜单是否收起状态 | boolean | - |
| inlineIndent | inline 模式的菜单缩进宽度 | number | 24 | | inlineIndent | inline 模式的菜单缩进宽度 | number | 24 |
| items | 菜单内容 | [ItemType\[\]](#ItemType) | - | | | items | 菜单内容 | [ItemType\[\]](#itemtype) | - | |
| mode | 菜单类型,现在支持垂直、水平、和内嵌模式三种 | `vertical` \| `horizontal` \| `inline` | `vertical` | | mode | 菜单类型,现在支持垂直、水平、和内嵌模式三种 | `vertical` \| `horizontal` \| `inline` | `vertical` |
| multiple | 是否允许多选 | boolean | false | | multiple | 是否允许多选 | boolean | false |
| openKeys(v-model) | 当前展开的 SubMenu 菜单项 key 数组 | (string \| number)[] | | | openKeys(v-model) | 当前展开的 SubMenu 菜单项 key 数组 | (string \| number)[] | |
@ -45,25 +45,25 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*Vn4XSqJFAxcAAA
### Menu 事件 ### Menu 事件
| 事件名称 | 说明 | 回调参数 | | 事件名称 | 说明 | 回调参数 |
| ---------- | ---------------------------------- | ------------------------------------- | | ---------- | ---------------------------------- | ---------------------------------------- |
| click | 点击 MenuItem 调用此函数 | function({ item, key, keyPath }) | | click | 点击 MenuItem 调用此函数 | function({ item, key, keyPath }) |
| deselect | 取消选中时调用,仅在 multiple 生效 | function({ item, key, selectedKeys }) | | deselect | 取消选中时调用,仅在 multiple 生效 | function({ item, key, selectedKeys }) |
| openChange | SubMenu 展开/关闭的回调 | function(openKeys: (string \| number)[]) | | openChange | SubMenu 展开/关闭的回调 | function(openKeys: (string \| number)[]) |
| select | 被选中时调用 | function({ item, key, selectedKeys }) | | select | 被选中时调用 | function({ item, key, selectedKeys }) |
### Menu.Item ### Menu.Item
| 参数 | 说明 | 类型 | 默认值 | 版本 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
| -------- | ------------------------ | -------------- | ------ | ----- | | -------- | ------------------------ | ---------------- | ------ | ----- |
| disabled | 是否禁用 | boolean | false | | | disabled | 是否禁用 | boolean | false | |
| icon | 菜单图标 | slot | | 2.8.0 | | icon | 菜单图标 | slot | | 2.8.0 |
| key | item 的唯一标志 | string \| number | | | | key | item 的唯一标志 | string \| number | | |
| title | 设置收缩时展示的悬浮标题 | string \| slot | | | | title | 设置收缩时展示的悬浮标题 | string \| slot | | |
### ItemType ### ItemType
> type ItemType = [MenuItemType](#MenuItemType) | [SubMenuType](#SubMenuType) | [MenuItemGroupType](#MenuItemGroupType) | [MenuDividerType](#MenuDividerType); > type ItemType = [MenuItemType](#menuitemtype) | [SubMenuType](#submenutype) | [MenuItemGroupType](#menuitemgrouptype) | [MenuDividerType](#menudividertype);
### MenuItemType ### MenuItemType
@ -72,7 +72,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*Vn4XSqJFAxcAAA
| danger | 展示错误状态样式 | boolean | false | | | danger | 展示错误状态样式 | boolean | false | |
| disabled | 是否禁用 | boolean | false | | | disabled | 是否禁用 | boolean | false | |
| icon | 菜单图标 | VueNode\|(item: MenuItemType)=>VueNode | - | | | icon | 菜单图标 | VueNode\|(item: MenuItemType)=>VueNode | - | |
| key | item 的唯一标志 | string \| number | - | | | key | item 的唯一标志 | string \| number | - | |
| label | 菜单项标题 | VueNode | - | | | label | 菜单项标题 | VueNode | - | |
| title | 设置收缩时展示的悬浮标题 | string | - | | | title | 设置收缩时展示的悬浮标题 | string | - | |
@ -80,7 +80,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*Vn4XSqJFAxcAAA
| 参数 | 说明 | 类型 | 默认值 | 版本 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| children | 子菜单的菜单项 | [ItemType\[\]](#ItemType) | - | | | children | 子菜单的菜单项 | [ItemType\[\]](#itemtype) | - | |
| disabled | 是否禁用 | boolean | false | | | disabled | 是否禁用 | boolean | false | |
| icon | 菜单图标 | VueNode\|(item: SubMenuType)=>VueNode | - | | | icon | 菜单图标 | VueNode\|(item: SubMenuType)=>VueNode | - | |
| key | 唯一标志 | string \| number | - | | | key | 唯一标志 | string \| number | - | |
@ -104,8 +104,8 @@ const groupItem = {
| 参数 | 说明 | 类型 | 默认值 | 版本 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
| -------- | ------------ | --------------------------------- | ------ | ---- | | -------- | ------------ | --------------------------------- | ------ | ---- |
| children | 分组的菜单项 | [MenuItemType\[\]](#MenuItemType) | - | | | children | 分组的菜单项 | [MenuItemType\[\]](#menuitemtype) | - | |
| label | 分组标题 | VueNode | - | | | label | 分组标题 | VueNode | - | |
#### MenuDividerType #### MenuDividerType
@ -128,7 +128,7 @@ const dividerItem = {
| disabled | 是否禁用 | boolean | false | | | disabled | 是否禁用 | boolean | false | |
| expandIcon | 自定义 Menu 展开收起图标 | slot | 箭头图标 | | | expandIcon | 自定义 Menu 展开收起图标 | slot | 箭头图标 | |
| icon | 菜单图标 | slot | | 2.8.0 | | icon | 菜单图标 | slot | | 2.8.0 |
| key | 唯一标志, 必填 | string \| number | | | | key | 唯一标志, 必填 | string \| number | | |
| popupClassName | 子菜单样式 | string | | 1.5.0 | | popupClassName | 子菜单样式 | string | | 1.5.0 |
| popupOffset | 子菜单偏移量,`mode="inline"` 时无效 | \[number, number] | - | | | popupOffset | 子菜单偏移量,`mode="inline"` 时无效 | \[number, number] | - | |
| title | 子菜单项值 | string\|slot | | | | title | 子菜单项值 | string\|slot | | |

View File

@ -1,5 +1,4 @@
import { computed, defineComponent, ref, watch } from 'vue'; import { computed, Transition, defineComponent, ref, watch } from 'vue';
import Transition from '../../_util/transition';
import { useInjectMenu, MenuContextProvider } from './hooks/useMenuContext'; import { useInjectMenu, MenuContextProvider } from './hooks/useMenuContext';
import type { MenuMode } from './interface'; import type { MenuMode } from './interface';
import SubMenuList from './SubMenuList'; import SubMenuList from './SubMenuList';

View File

@ -112,7 +112,7 @@ export default defineComponent({
return !override; return !override;
}), }),
); );
const store = shallowRef<Map<string, StoreMenuInfo>>(new Map()); const store = shallowRef(new Map<string, StoreMenuInfo>());
const siderCollapsed = inject(SiderCollapsedKey, ref(undefined)); const siderCollapsed = inject(SiderCollapsedKey, ref(undefined));
const inlineCollapsed = computed(() => { const inlineCollapsed = computed(() => {
if (siderCollapsed.value !== undefined) { if (siderCollapsed.value !== undefined) {
@ -341,7 +341,7 @@ export default defineComponent({
const rootPrefixCls = computed(() => getPrefixCls()); const rootPrefixCls = computed(() => getPrefixCls());
const defaultMotions = computed(() => ({ const defaultMotions = computed(() => ({
horizontal: { name: `${rootPrefixCls.value}-slide-up` }, horizontal: { name: `${rootPrefixCls.value}-slide-up` },
inline: collapseMotion, inline: collapseMotion(`${rootPrefixCls.value}-motion-collapse`),
other: { name: `${rootPrefixCls.value}-zoom-big` }, other: { name: `${rootPrefixCls.value}-zoom-big` },
})); }));
@ -439,15 +439,17 @@ export default defineComponent({
forceSubMenuRender: computed(() => props.forceSubMenuRender), forceSubMenuRender: computed(() => props.forceSubMenuRender),
rootClassName: hashId, rootClassName: hashId,
}); });
const getChildrenList = () => itemsNodes.value || flattenChildren(slots.default?.());
return () => { return () => {
const childList = itemsNodes.value || flattenChildren(slots.default?.()); const childList = getChildrenList();
const allVisible = const allVisible =
lastVisibleIndex.value >= childList.length - 1 || lastVisibleIndex.value >= childList.length - 1 ||
mergedMode.value !== 'horizontal' || mergedMode.value !== 'horizontal' ||
props.disabledOverflow; props.disabledOverflow;
// >>>>> Children // >>>>> Children
const wrappedChildList = const getWrapperList = childList => {
mergedMode.value !== 'horizontal' || props.disabledOverflow return mergedMode.value !== 'horizontal' || props.disabledOverflow
? childList ? childList
: // Need wrap for overflow dropdown that do not response for open : // Need wrap for overflow dropdown that do not response for open
childList.map((child, index) => ( childList.map((child, index) => (
@ -458,6 +460,7 @@ export default defineComponent({
v-slots={{ default: () => child }} v-slots={{ default: () => child }}
></MenuContextProvider> ></MenuContextProvider>
)); ));
};
const overflowedIndicator = slots.overflowedIndicator?.() || <EllipsisOutlined />; const overflowedIndicator = slots.overflowedIndicator?.() || <EllipsisOutlined />;
return wrapSSR( return wrapSSR(
@ -470,7 +473,7 @@ export default defineComponent({
class={[className.value, attrs.class, hashId.value]} class={[className.value, attrs.class, hashId.value]}
role="menu" role="menu"
id={props.id} id={props.id}
data={wrappedChildList} data={getWrapperList(childList)}
renderRawItem={node => node} renderRawItem={node => node}
renderRawRest={omitItems => { renderRawRest={omitItems => {
// We use origin list since wrapped list use context to prevent open // We use origin list since wrapped list use context to prevent open
@ -514,7 +517,7 @@ export default defineComponent({
> >
<Teleport to="body"> <Teleport to="body">
<div style={{ display: 'none' }} aria-hidden> <div style={{ display: 'none' }} aria-hidden>
<PathContext>{wrappedChildList}</PathContext> <PathContext>{getWrapperList(getChildrenList())}</PathContext>
</div> </div>
</Teleport> </Teleport>
</Overflow>, </Overflow>,

View File

@ -116,7 +116,7 @@ function convertItemsToNodes(
export default function useItems(props: MenuProps) { export default function useItems(props: MenuProps) {
const itemsNodes = shallowRef([]); const itemsNodes = shallowRef([]);
const hasItmes = shallowRef(false); const hasItmes = shallowRef(false);
const store = shallowRef<Map<string, StoreMenuInfo>>(new Map()); const store = shallowRef(new Map<string, StoreMenuInfo>());
watch( watch(
() => props.items, () => props.items,
() => { () => {

View File

@ -32,7 +32,7 @@ export interface MenuContextProps {
inlineCollapsed: Ref<boolean>; inlineCollapsed: Ref<boolean>;
theme?: ComputedRef<MenuTheme>; theme?: ComputedRef<MenuTheme>;
siderCollapsed?: ComputedRef<boolean>; siderCollapsed?: Ref<boolean>;
// // Mode // // Mode
mode: Ref<MenuMode>; mode: Ref<MenuMode>;

View File

@ -47,7 +47,9 @@ const Holder = defineComponent({
'rtl', 'rtl',
'transitionName', 'transitionName',
'onAllRemoved', 'onAllRemoved',
] as any, 'animation',
'staticGetContainer',
],
setup(props, { expose }) { setup(props, { expose }) {
const { getPrefixCls, getPopupContainer } = useConfigInject('message', props); const { getPrefixCls, getPopupContainer } = useConfigInject('message', props);

View File

@ -3,6 +3,7 @@ import ConfirmDialog from './ConfirmDialog';
import type { ModalFuncProps } from './Modal'; import type { ModalFuncProps } from './Modal';
import ConfigProvider, { globalConfigForApi } from '../config-provider'; import ConfigProvider, { globalConfigForApi } from '../config-provider';
import omit from '../_util/omit'; import omit from '../_util/omit';
import { triggerVNodeUpdate } from '../_util/vnode';
import { getConfirmLocale } from './locale'; import { getConfirmLocale } from './locale';
import destroyFns from './destroyFns'; import destroyFns from './destroyFns';
@ -27,7 +28,6 @@ const confirm = (config: ModalFuncProps) => {
if (confirmDialogInstance) { if (confirmDialogInstance) {
// destroy // destroy
vueRender(null, container as any); vueRender(null, container as any);
confirmDialogInstance.component.update();
confirmDialogInstance = null; confirmDialogInstance = null;
} }
const triggerCancel = args.some(param => param && param.triggerCancel); const triggerCancel = args.some(param => param && param.triggerCancel);
@ -70,8 +70,7 @@ const confirm = (config: ModalFuncProps) => {
}; };
} }
if (confirmDialogInstance) { if (confirmDialogInstance) {
Object.assign(confirmDialogInstance.component.props, currentConfig); triggerVNodeUpdate(confirmDialogInstance, currentConfig, container);
confirmDialogInstance.component.update();
} }
} }

View File

@ -72,8 +72,8 @@ The items listed above are all functions, expecting a settings object as paramet
| centered | Centered Modal | boolean | `false` | | | centered | Centered Modal | boolean | `false` | |
| class | class of container | string | - | | | class | class of container | string | - | |
| closable | Whether a close (x) button is visible on top right of the modal dialog or not | boolean | `false` | | | closable | Whether a close (x) button is visible on top right of the modal dialog or not | boolean | `false` | |
| content | Content | string\|VNode \|function(h) | - | | | content | Content | string\|VNode \|function() | - | |
| footer | Footer content, set as `footer: null` when you don't need default buttons | string\|VNode \|function(h) | - | 4.0.0 | | footer | Footer content, set as `footer: null` when you don't need default buttons | string\|VNode \|function() | - | 4.0.0 |
| icon | custom icon (`Added in 1.14.0`) | VNode \|()=>VNode | - | | | icon | custom icon (`Added in 1.14.0`) | VNode \|()=>VNode | - | |
| keyboard | Whether support press esc to close | boolean | true | | | keyboard | Whether support press esc to close | boolean | true | |
| mask | Whether show mask or not. | boolean | true | | | mask | Whether show mask or not. | boolean | true | |
@ -81,7 +81,7 @@ The items listed above are all functions, expecting a settings object as paramet
| okButtonProps | The ok button props | [ButtonProps](/components/button) | - | | | okButtonProps | The ok button props | [ButtonProps](/components/button) | - | |
| okText | Text of the OK button | string | `OK` | | | okText | Text of the OK button | string | `OK` | |
| okType | Button `type` of the OK button | string | `primary` | | | okType | Button `type` of the OK button | string | `primary` | |
| title | Title | string\|VNode \|function(h) | - | | | title | Title | string\|VNode \|function() | - | |
| width | Width of the modal dialog | string\|number | 416 | | | width | Width of the modal dialog | string\|number | 416 | |
| wrapClassName | The class name of the container of the modal dialog | string | - | 3.2.3 | | wrapClassName | The class name of the container of the modal dialog | string | - | 3.2.3 |
| zIndex | The `z-index` of the Modal | number | 1000 | | | zIndex | The `z-index` of the Modal | number | 1000 | |
@ -155,3 +155,5 @@ When you need context information (for example, using a globally registered comp
}); });
</script> </script>
``` ```
> [App Package Component](/components/app) can be used to simplify the problem of `useModal` and other methods that need to manually implant contextHolder.

View File

@ -19,14 +19,14 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*fBrgSJBmavgAAA
| 参数 | 说明 | 类型 | 默认值 | 版本 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| afterClose | Modal 完全关闭后的回调 | function | | | | afterClose | Modal 完全关闭后的回调 | function | - | |
| bodyStyle | Modal body 样式 | object | {} | | | bodyStyle | Modal body 样式 | object | {} | |
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button/#api) | - | | | cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button/#api) | - | |
| cancelText | 取消按钮文字 | string\| slot | 取消 | | | cancelText | 取消按钮文字 | string\| slot | 取消 | |
| centered | 垂直居中展示 Modal | boolean | `false` | | | centered | 垂直居中展示 Modal | boolean | `false` | |
| closable | 是否显示右上角的关闭按钮 | boolean | true | | | closable | 是否显示右上角的关闭按钮 | boolean | true | |
| closeIcon | 自定义关闭图标 | VNode \| slot | - | | | closeIcon | 自定义关闭图标 | VNode \| slot | - | |
| confirmLoading | 确定按钮 loading | boolean | | | | confirmLoading | 确定按钮 loading | boolean | - | |
| destroyOnClose | 关闭时销毁 Modal 里的子元素 | boolean | false | | | destroyOnClose | 关闭时销毁 Modal 里的子元素 | boolean | false | |
| footer | 底部内容,当不需要默认底部按钮时,可以设为 `:footer="null"` | string\|slot | 确定取消按钮 | | | footer | 底部内容,当不需要默认底部按钮时,可以设为 `:footer="null"` | string\|slot | 确定取消按钮 | |
| forceRender | 强制渲染 Modal | boolean | false | | | forceRender | 强制渲染 Modal | boolean | false | |
@ -38,8 +38,8 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*fBrgSJBmavgAAA
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button/#api) | - | | | okButtonProps | ok 按钮 props | [ButtonProps](/components/button/#api) | - | |
| okText | 确认按钮文字 | string\|slot | 确定 | | | okText | 确认按钮文字 | string\|slot | 确定 | |
| okType | 确认按钮类型 | string | primary | | | okType | 确认按钮类型 | string | primary | |
| title | 标题 | string\|slot | | | | title | 标题 | string\|slot | - | |
| open(v-model) | 对话框是否可见 | boolean | | | | open(v-model) | 对话框是否可见 | boolean | - | |
| width | 宽度 | string\|number | 520 | | | width | 宽度 | string\|number | 520 | |
| wrapClassName | 对话框外层容器的类名 | string | - | | | wrapClassName | 对话框外层容器的类名 | string | - | |
| zIndex | 设置 Modal 的 `z-index` | number | 1000 | | | zIndex | 设置 Modal 的 `z-index` | number | 1000 | |
@ -76,8 +76,8 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*fBrgSJBmavgAAA
| centered | 垂直居中展示 Modal | boolean | `false` | | | centered | 垂直居中展示 Modal | boolean | `false` | |
| class | 容器类名 | string | - | | | class | 容器类名 | string | - | |
| closable | 是否显示右上角的关闭按钮 | boolean | `false` | | | closable | 是否显示右上角的关闭按钮 | boolean | `false` | |
| content | 内容 | string \|VNode \|function(h) | 无 | | | content | 内容 | string \|VNode \|function() | - | |
| footer | 底部内容,当不需要默认底部按钮时,可以设为 `footer: null` | string \|VNode \|function(h) | - | 4.0.0 | | footer | 底部内容,当不需要默认底部按钮时,可以设为 `footer: null` | string \|VNode \|function() | - | 4.0.0 |
| icon | 自定义图标1.14.0 新增) | VNode \| ()=>VNode | - | | | icon | 自定义图标1.14.0 新增) | VNode \| ()=>VNode | - | |
| keyboard | 是否支持键盘 esc 关闭 | boolean | true | | | keyboard | 是否支持键盘 esc 关闭 | boolean | true | |
| mask | 是否展示遮罩 | boolean | true | | | mask | 是否展示遮罩 | boolean | true | |
@ -85,12 +85,12 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*fBrgSJBmavgAAA
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button) | - | | | okButtonProps | ok 按钮 props | [ButtonProps](/components/button) | - | |
| okText | 确认按钮文字 | string | 确定 | | | okText | 确认按钮文字 | string | 确定 | |
| okType | 确认按钮类型 | string | primary | | | okType | 确认按钮类型 | string | primary | |
| title | 标题 | string\|VNode \|function(h) | 无 | | | title | 标题 | string\|VNode \|function() | - | |
| width | 宽度 | string\|number | 416 | | | width | 宽度 | string\|number | 416 | |
| wrapClassName | 对话框外层容器的类名 | string | - | 3.2.3 | | wrapClassName | 对话框外层容器的类名 | string | - | 3.2.3 |
| zIndex | 设置 Modal 的 `z-index` | number | 1000 | | | zIndex | 设置 Modal 的 `z-index` | number | 1000 | |
| onCancel | 取消回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function | | | | onCancel | 取消回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function | - | |
| onOk | 点击确定回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function | | | | onOk | 点击确定回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function | - | |
以上函数调用后,会返回一个引用,可以通过该引用更新和关闭弹窗。 以上函数调用后,会返回一个引用,可以通过该引用更新和关闭弹窗。
@ -159,3 +159,5 @@ router.beforeEach((to, from, next) => {
}); });
</script> </script>
``` ```
> 可通过 [App 包裹组件](/components/app-cn) 简化`useModal`等方法需要手动植入 contextHolder 的问题。

View File

@ -8,7 +8,7 @@ title:
## zh-CN ## zh-CN
通过 `notification.useNotification` 创建支持读取 context `contextHolder`请注意我们推荐通过顶层注册的方式代替 `message` 静态方法因为静态方法无法消费上下文因而 ConfigProvider 的数据也不会生效 通过 `notification.useNotification` 创建支持读取 context `contextHolder`请注意我们推荐通过顶层注册的方式代替 `notification` 静态方法因为静态方法无法消费上下文因而 ConfigProvider 的数据也不会生效
## en-US ## en-US

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