diff --git a/.eslintrc.js b/.eslintrc.js index face650ee7..d6b5da49da 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -27,6 +27,7 @@ module.exports = { // ], plugins: ['markdown', 'jest', '@typescript-eslint', 'import'], globals: { + h: true, defineProps: 'readonly', }, overrides: [ @@ -108,7 +109,4 @@ module.exports = { ], 'vue/multi-word-component-names': 'off', }, - globals: { - h: true, - }, }; diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 32718a94d1..00c687e3b4 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: false +blank_issues_enabled: true contact_links: - name: Create new issue url: https://vuecomponent.github.io/issue-helper/ diff --git a/.github/workflows/issue-labeled.yml b/.github/workflows/issue-labeled.yml index b5199b61a0..ad5aac3950 100644 --- a/.github/workflows/issue-labeled.yml +++ b/.github/workflows/issue-labeled.yml @@ -14,7 +14,7 @@ jobs: pull-requests: write # for actions-cool/issues-helper to update PRs runs-on: ubuntu-latest steps: - - name: Need Reproduce + - name: Need Reproduce if: github.event.label.name == '🤔 Need Reproduce' uses: actions-cool/issues-helper@v3 with: diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index a3275a2936..84d4ffa90c 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -10,6 +10,76 @@ --- +## 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 Pagination’s 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) diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index af90dade15..c9b50d9631 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -10,6 +10,76 @@ --- +## 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) diff --git a/README-zh_CN.md b/README-zh_CN.md index d894b6576d..05a8f70302 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -10,7 +10,7 @@
-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) @@ -73,6 +73,7 @@ $ yarn add ant-design-vue | [vue-dash-event](https://github.com/vueComponent/vue-dash-event) | 在 DOM 模板中,您可以使用 ant-design-vue 组件的自定义事件(camelCase) | | [@formily/antdv](https://github.com/formilyjs/antdv) | 这是一个结合了 Formily 和 ant-design-vue 的组件库 | | [@ant-design-vue/nuxt](https://github.com/vueComponent/ant-design-vue-nuxt) | ant-design-vue 的 nuxt 模块扩展 | +| [ant-design-x-vue](https://github.com/wzc520pyfm/ant-design-x-vue) | 基于 Ant Design X 设计规范的 Vue AI 界面解决方案 | ## 问答 @@ -88,22 +89,23 @@ ant-design-vue 是 MIT 协议的开源项目。为了项目能够更好的持续 - [opencollective](https://opencollective.com/ant-design-vue) - [paypal](https://www.paypal.me/tangjinzhou) - [支付宝或微信](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)] -## 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)] ## 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)] diff --git a/README.md b/README.md index 03eff07fcc..3bc3fb0fc3 100644 --- a/README.md +++ b/README.md @@ -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) | | [@formily/antdv](https://github.com/formilyjs/antdv) | The Library with Formily and ant-design-vue | | [@ant-design-vue/nuxt](https://github.com/vueComponent/ant-design-vue-nuxt) | A nuxt module for ant-design-vue | +| [ant-design-x-vue](https://github.com/wzc520pyfm/ant-design-x-vue) | A Vue AI interface solutions base on the Ant Design X design specification | ## Donation @@ -82,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) - [paypal](https://www.paypal.me/tangjinzhou) - [支付宝或微信](https://aliyuncdn.antdv.com/alipay-and-wechat.png) +- ETH: 0x30cc48515d8ae9fefa20ab87226ad7e8ab9c3bc2 ## Sponsors diff --git a/antd-tools/generator-types/src/index.ts b/antd-tools/generator-types/src/index.ts index 4b1e3d43ff..aad960d39a 100644 --- a/antd-tools/generator-types/src/index.ts +++ b/antd-tools/generator-types/src/index.ts @@ -6,7 +6,6 @@ import { genWebTypes } from './web-types'; import { outputFileSync, readFileSync } from 'fs-extra'; import type { Options, VueTag } from './type'; import { getComponentName, normalizePath, toKebabCase } from './utils'; -import { genVeturAttributes, genVeturTags } from './vetur'; import { flatMap } from 'lodash'; async function readMarkdown(options: Options): Promise> { @@ -22,13 +21,13 @@ async function readMarkdown(options: Options): Promise> { return formatter(mdParser(fileContent), componentName, kebabComponentName, options.tagPrefix); }) .filter(item => item) as VueTag[][]; - const tags: Map = new Map(); + const tags = new Map(); flatMap(data, item => item).forEach(mergedTag => mergeTag(tags, mergedTag)); return tags; } function readTypings(options: Options): Map { - const tags: Map = new Map(); + const tags = new Map(); const fileContent = readFileSync(options.typingsPath, 'utf-8'); fileContent .split('\n') @@ -62,7 +61,7 @@ function mergeTag(tags: Map, mergedTag: VueTag) { function mergeTags(mergedTagsArr: Map[]): VueTag[] { if (mergedTagsArr.length === 1) return [...mergedTagsArr[0].values()]; - const tags: Map = new Map(); + const tags = new Map(); if (mergedTagsArr.length === 0) return []; mergedTagsArr.forEach(mergedTags => { mergedTags.forEach(mergedTag => mergeTag(tags, mergedTag)); @@ -78,13 +77,6 @@ export async function parseAndWrite(options: Options): Promise { const tagsFromTypings = await readTypings(options); const tags = mergeTags([tagsFromMarkdown, tagsFromTypings]); const webTypes = genWebTypes(tags, options); - const veturTags = genVeturTags(tags); - const veturAttributes = genVeturAttributes(tags); - outputFileSync(join(options.outputDir, 'tags.json'), JSON.stringify(veturTags, null, 2)); - outputFileSync( - join(options.outputDir, 'attributes.json'), - JSON.stringify(veturAttributes, null, 2), - ); outputFileSync(join(options.outputDir, 'web-types.json'), JSON.stringify(webTypes, null, 2)); return tags.length; } diff --git a/antd-tools/generator-types/src/parser.ts b/antd-tools/generator-types/src/parser.ts index 59f7d2483f..bdcafb3d50 100644 --- a/antd-tools/generator-types/src/parser.ts +++ b/antd-tools/generator-types/src/parser.ts @@ -21,7 +21,7 @@ export type Articals = Artical[]; function readLine(input: string) { 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) { @@ -47,7 +47,7 @@ function tableParse(input: string) { }; while (start < end) { - const target = input.substr(start); + const target = input.substring(start); const line = readLine(target); if (!/^\|/.test(target)) { @@ -79,7 +79,7 @@ export function mdParser(input: string): Articals { const end = input.length; while (start < end) { - const target = input.substr(start); + const target = input.substring(start); let match; if ((match = TITLE_REG.exec(target))) { @@ -91,7 +91,7 @@ export function mdParser(input: string): Articals { start += match.index + match[0].length; } else if ((match = TABLE_REG.exec(target))) { - const { table, usedLength } = tableParse(target.substr(match.index)); + const { table, usedLength } = tableParse(target.substring(match.index)); artical.push({ type: 'table', table, diff --git a/antd-tools/generator-types/src/type.ts b/antd-tools/generator-types/src/type.ts index faf2ed939a..6be1c0ba5e 100644 --- a/antd-tools/generator-types/src/type.ts +++ b/antd-tools/generator-types/src/type.ts @@ -34,25 +34,6 @@ export type VueTag = { description?: string; }; -export type VeturTag = { - description?: string; - attributes: string[]; -}; - -export type VeturTags = Record; - -export type VeturAttribute = { - type: string; - description: string; -}; - -export type VeturAttributes = Record; - -export type VeturResult = { - tags: VeturTags; - attributes: VeturAttributes; -}; - export type Options = { name: string; path: PathLike; diff --git a/antd-tools/generator-types/src/vetur.ts b/antd-tools/generator-types/src/vetur.ts deleted file mode 100644 index da34458230..0000000000 --- a/antd-tools/generator-types/src/vetur.ts +++ /dev/null @@ -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; -} diff --git a/antd-tools/getTSCommonConfig.js b/antd-tools/getTSCommonConfig.js index d935888c4d..fd2a33ff61 100644 --- a/antd-tools/getTSCommonConfig.js +++ b/antd-tools/getTSCommonConfig.js @@ -1,7 +1,6 @@ 'use strict'; const fs = require('fs'); -const assign = require('object-assign'); const { getProjectPath } = require('./utils/projectHelper'); module.exports = function () { @@ -9,7 +8,7 @@ module.exports = function () { if (fs.existsSync(getProjectPath('tsconfig.json'))) { my = require(getProjectPath('tsconfig.json')); } - return assign( + return Object.assign( { noUnusedParameters: true, noUnusedLocals: true, diff --git a/components/_util/BaseInput.tsx b/components/_util/BaseInput.tsx index db85e3a4f2..e6bc9d7085 100644 --- a/components/_util/BaseInput.tsx +++ b/components/_util/BaseInput.tsx @@ -1,51 +1,167 @@ -import { defineComponent, shallowRef, withDirectives } from 'vue'; -import antInput from './antInputDirective'; +import type { PropType } from 'vue'; +import { computed, defineComponent, shallowRef, ref, watch } from 'vue'; import PropTypes from './vue-types'; +import type { BaseInputInnerExpose } from './BaseInputInner'; +import BaseInputInner from './BaseInputInner'; +import { styleObjectToString } from '../vc-util/Dom/css'; + +export interface BaseInputExpose { + focus: () => void; + blur: () => void; + input: HTMLInputElement | HTMLTextAreaElement | null; + setSelectionRange: ( + start: number, + end: number, + direction?: 'forward' | 'backward' | 'none', + ) => void; + select: () => void; + getSelectionStart: () => number | null; + getSelectionEnd: () => number | null; + getScrollTop: () => number | null; + setScrollTop: (scrollTop: number) => void; +} const BaseInput = defineComponent({ compatConfig: { MODE: 3 }, + inheritAttrs: false, props: { - 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'], - setup(_p, { emit }) { - const inputRef = shallowRef(null); + emits: [ + 'change', + 'input', + 'blur', + 'keydown', + 'focus', + 'compositionstart', + 'compositionend', + 'keyup', + 'paste', + 'mousedown', + ], + setup(props, { emit, attrs, expose }) { + const inputRef = shallowRef(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 { composing } = e.target as any; - if ((e as any).isComposing || composing) { - emit('input', e); - } else { - emit('input', e); - emit('change', e); + emit('change', e); + }; + const onCompositionstart = (e: CompositionEvent) => { + isComposing.value = true; + (e.target as any).composing = true; + emit('compositionstart', e); + }; + const onCompositionend = (e: CompositionEvent) => { + isComposing.value = false; + (e.target as any).composing = false; + emit('compositionend', e); + const event = document.createEvent('HTMLEvents'); + event.initEvent('input', true, true); + e.target.dispatchEvent(event); + handleChange(e); + }; + const handleInput = (e: Event) => { + if (isComposing.value && props.lazy) { + renderValue.value = (e.target as HTMLInputElement).value; + return; } + emit('input', e); }; - return { - inputRef, - focus: () => { - if (inputRef.value) { - inputRef.value.focus(); - } - }, - blur: () => { - if (inputRef.value) { - inputRef.value.blur(); - } - }, - handleChange, + + const handleBlur = (e: Event) => { + emit('blur', e); }; - }, - render() { - return withDirectives( - ( - { + emit('focus', e); + }; + + const focus = () => { + if (inputRef.value) { + inputRef.value.focus(); + } + }; + const blur = () => { + if (inputRef.value) { + inputRef.value.blur(); + } + }; + const handleKeyDown = (e: KeyboardEvent) => { + emit('keydown', e); + }; + const handleKeyUp = (e: KeyboardEvent) => { + emit('keyup', e); + }; + const setSelectionRange = ( + start: number, + end: number, + direction?: 'forward' | 'backward' | 'none', + ) => { + inputRef.value?.setSelectionRange(start, end, direction); + }; + + const select = () => { + inputRef.value?.select(); + }; + expose({ + focus, + blur, + input: computed(() => inputRef.value?.input), + setSelectionRange, + select, + getSelectionStart: () => inputRef.value?.getSelectionStart(), + getSelectionEnd: () => inputRef.value?.getSelectionEnd(), + getScrollTop: () => inputRef.value?.getScrollTop(), + }); + const handleMousedown = (e: MouseEvent) => { + emit('mousedown', e); + }; + const handlePaste = (e: ClipboardEvent) => { + emit('paste', e); + }; + const styleString = computed(() => { + return props.style && typeof props.style !== 'string' + ? styleObjectToString(props.style) + : props.style; + }); + return () => { + const { style, lazy, ...restProps } = props; + return ( + - ) as any, - [[antInput]], - ); + ); + }; }, }); diff --git a/components/_util/BaseInputInner.tsx b/components/_util/BaseInputInner.tsx new file mode 100644 index 0000000000..10423d7a45 --- /dev/null +++ b/components/_util/BaseInputInner.tsx @@ -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 ; + }; + }, +}); + +export default BaseInputInner; diff --git a/components/_util/PortalWrapper.tsx b/components/_util/PortalWrapper.tsx index 1ef3d6e7ce..91561990bf 100644 --- a/components/_util/PortalWrapper.tsx +++ b/components/_util/PortalWrapper.tsx @@ -7,7 +7,6 @@ import { onMounted, onBeforeUnmount, onUpdated, - getCurrentInstance, nextTick, computed, } from 'vue'; @@ -61,6 +60,7 @@ export default defineComponent({ const container = shallowRef(); const componentRef = shallowRef(); const rafId = shallowRef(); + const triggerUpdate = shallowRef(1); const defaultContainer = canUseDom() && document.createElement('div'); const removeCurrentContainer = () => { // Portal will remove from `parentNode`. @@ -106,8 +106,6 @@ export default defineComponent({ attachToParent(); }); - const instance = getCurrentInstance(); - useScrollLocker( computed(() => { return ( @@ -155,7 +153,7 @@ export default defineComponent({ nextTick(() => { if (!attachToParent()) { rafId.value = raf(() => { - instance.update(); + triggerUpdate.value += 1; }); } }); @@ -177,7 +175,7 @@ export default defineComponent({ getOpenCount: () => openCount, getContainer, }; - if (forceRender || visible || componentRef.value) { + if (triggerUpdate.value && (forceRender || visible || componentRef.value)) { portal = ( { + return customRenderSlot(slots, 'default', {}, () => ['default value']); + }; + }, +}); diff --git a/components/_util/__tests__/vNode.test.js b/components/_util/__tests__/vNode.test.js new file mode 100644 index 0000000000..4bfc7e8fa9 --- /dev/null +++ b/components/_util/__tests__/vNode.test.js @@ -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'); + }); +}); diff --git a/components/_util/antInputDirective.ts b/components/_util/antInputDirective.ts deleted file mode 100644 index 204390f4c0..0000000000 --- a/components/_util/antInputDirective.ts +++ /dev/null @@ -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; diff --git a/components/_util/css-animation/Event.js b/components/_util/css-animation/Event.js deleted file mode 100644 index cd5e871552..0000000000 --- a/components/_util/css-animation/Event.js +++ /dev/null @@ -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; diff --git a/components/_util/css-animation/index.js b/components/_util/css-animation/index.js deleted file mode 100644 index 86e399dec9..0000000000 --- a/components/_util/css-animation/index.js +++ /dev/null @@ -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; diff --git a/components/_util/cssinjs/hooks/useCacheToken.tsx b/components/_util/cssinjs/hooks/useCacheToken.tsx index 9a11fb225f..f97d146b55 100644 --- a/components/_util/cssinjs/hooks/useCacheToken.tsx +++ b/components/_util/cssinjs/hooks/useCacheToken.tsx @@ -8,9 +8,13 @@ import { ref, computed } from 'vue'; const EMPTY_OVERRIDE = {}; +const isProduction = process.env.NODE_ENV === 'production'; +// nuxt generate when NODE_ENV is prerender +const isPrerender = process.env.NODE_ENV === 'prerender'; + // Generate different prefix to make user selector break in production env. // This helps developer not to do style override directly on the hash id. -const hashPrefix = 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 { /** diff --git a/components/_util/cssinjs/hooks/useStyleRegister/index.tsx b/components/_util/cssinjs/hooks/useStyleRegister/index.tsx index e966032384..d264d0744e 100644 --- a/components/_util/cssinjs/hooks/useStyleRegister/index.tsx +++ b/components/_util/cssinjs/hooks/useStyleRegister/index.tsx @@ -42,17 +42,17 @@ export type CSSProperties = Omit, 'anima export type CSSPropertiesWithMultiValues = { [K in keyof CSSProperties]: | CSSProperties[K] - | Extract[] + | readonly Extract[] | { - [SKIP_CHECK]: boolean; + [SKIP_CHECK]?: boolean; [MULTI_VALUE]?: boolean; - value: CSSProperties[K] | Extract[]; + value: CSSProperties[K] | CSSProperties[K][]; }; }; export type CSSPseudos = { [K in CSS.Pseudos]?: CSSObject }; -type ArrayCSSInterpolation = CSSInterpolation[]; +type ArrayCSSInterpolation = readonly CSSInterpolation[]; export type InterpolationPrimitive = null | undefined | boolean | number | string | CSSObject; diff --git a/components/_util/getScroll.ts b/components/_util/getScroll.ts index 2889b43ed7..ca0b10005d 100644 --- a/components/_util/getScroll.ts +++ b/components/_util/getScroll.ts @@ -12,7 +12,7 @@ export default function getScroll( const method = top ? 'scrollTop' : 'scrollLeft'; let result = 0; if (isWindow(target)) { - result = target[top ? 'pageYOffset' : 'pageXOffset']; + result = target[top ? 'scrollY' : 'scrollX']; } else if (target instanceof Document) { result = target.documentElement[method]; } else if (target instanceof HTMLElement) { diff --git a/components/_util/hooks/_vueuse/_configurable.ts b/components/_util/hooks/_vueuse/_configurable.ts index 858fccc7d0..a32d339399 100644 --- a/components/_util/hooks/_vueuse/_configurable.ts +++ b/components/_util/hooks/_vueuse/_configurable.ts @@ -28,7 +28,7 @@ export interface ConfigurableLocation { location?: Location; } -export const defaultWindow = isClient ? window : undefined; -export const defaultDocument = isClient ? window.document : undefined; -export const defaultNavigator = isClient ? window.navigator : undefined; -export const defaultLocation = isClient ? window.location : undefined; +export const defaultWindow = isClient ? window : undefined; +export const defaultDocument = isClient ? window.document : undefined; +export const defaultNavigator = isClient ? window.navigator : undefined; +export const defaultLocation = isClient ? window.location : undefined; diff --git a/components/_util/hooks/_vueuse/is.ts b/components/_util/hooks/_vueuse/is.ts index a17b114285..1acf122e8f 100644 --- a/components/_util/hooks/_vueuse/is.ts +++ b/components/_util/hooks/_vueuse/is.ts @@ -21,8 +21,6 @@ export const rand = (min: number, max: number) => { return Math.floor(Math.random() * (max - min + 1)) + min; }; export const isIOS = - isClient && - window?.navigator?.userAgent && - /iP(ad|hone|od)/.test(window.navigator.userAgent); + isClient && window?.navigator?.userAgent && /iP(ad|hone|od)/.test(window.navigator.userAgent); export const hasOwn = (val: T, key: K): key is K => Object.prototype.hasOwnProperty.call(val, key); diff --git a/components/_util/isCssAnimationSupported.js b/components/_util/isCssAnimationSupported.js deleted file mode 100644 index 45d51bb356..0000000000 --- a/components/_util/isCssAnimationSupported.js +++ /dev/null @@ -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; diff --git a/components/_util/scrollTo.ts b/components/_util/scrollTo.ts index c9dbb89104..992d6a930e 100644 --- a/components/_util/scrollTo.ts +++ b/components/_util/scrollTo.ts @@ -22,8 +22,8 @@ export default function scrollTo(y: number, options: ScrollToOptions = {}) { const time = timestamp - startTime; const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration); if (isWindow(container)) { - (container as Window).scrollTo(window.pageXOffset, nextScrollTop); - } else if (container instanceof Document || container.constructor.name === 'HTMLDocument') { + (container as Window).scrollTo(window.scrollX, nextScrollTop); + } else if (container instanceof Document) { (container as Document).documentElement.scrollTop = nextScrollTop; } else { (container as HTMLElement).scrollTop = nextScrollTop; diff --git a/components/_util/transition.tsx b/components/_util/transition.tsx index fd6ebc4904..f4b69f31ed 100644 --- a/components/_util/transition.tsx +++ b/components/_util/transition.tsx @@ -5,7 +5,7 @@ import type { TransitionGroupProps, TransitionProps, } from 'vue'; -import { nextTick, Transition, TransitionGroup } from 'vue'; +import { nextTick } from 'vue'; import { tuple } from './type'; const SelectPlacements = tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight'); @@ -126,6 +126,4 @@ const getTransitionName = (rootPrefixCls: string, motion: string, transitionName return `${rootPrefixCls}-${motion}`; }; -export { Transition, TransitionGroup, collapseMotion, getTransitionName, getTransitionDirection }; - -export default Transition; +export { collapseMotion, getTransitionName, getTransitionDirection }; diff --git a/components/_util/triggerEvent.ts b/components/_util/triggerEvent.ts deleted file mode 100644 index 3c8cfc5414..0000000000 --- a/components/_util/triggerEvent.ts +++ /dev/null @@ -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); - } -} diff --git a/components/_util/type.ts b/components/_util/type.ts index 0965d15ae3..ed5541d4e5 100644 --- a/components/_util/type.ts +++ b/components/_util/type.ts @@ -33,7 +33,7 @@ export interface PropOptions { declare type VNodeChildAtom = VNode | string | number | boolean | null | undefined | void; // eslint-disable-next-line no-undef -export type VueNode = VNodeChildAtom | VNodeChildAtom[] | JSX.Element; +export type VueNode = VNodeChildAtom | VNodeChildAtom[] | VNode; export const withInstall = (comp: T) => { const c = comp as any; diff --git a/components/_util/vnode.ts b/components/_util/vnode.ts index 1f8265c2d9..8565cbe5ba 100644 --- a/components/_util/vnode.ts +++ b/components/_util/vnode.ts @@ -1,6 +1,6 @@ import { filterEmpty } from './props-util'; -import type { VNode, VNodeProps } from 'vue'; -import { cloneVNode, isVNode } from 'vue'; +import type { Slots, VNode, VNodeArrayChildren, VNodeProps } from 'vue'; +import { cloneVNode, isVNode, Comment, Fragment, render as VueRender } from 'vue'; import warning from './warning'; import type { RefObject } from './createRef'; type NodeProps = Record & @@ -51,3 +51,32 @@ export function deepCloneElement( return cloned; } } + +export function triggerVNodeUpdate(vm: VNode, attrs: Record, 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, + fallback?: () => VNodeArrayChildren, +) { + const slot = slots[name]?.(props); + if (ensureValidVNode(slot)) { + return slot; + } + return fallback?.(); +} diff --git a/components/_util/wave/WaveEffect.tsx b/components/_util/wave/WaveEffect.tsx index 1f3ce23d17..b59c8ff66a 100644 --- a/components/_util/wave/WaveEffect.tsx +++ b/components/_util/wave/WaveEffect.tsx @@ -159,6 +159,12 @@ function showWaveEffect(node: HTMLElement, className: string) { node?.insertBefore(holder, node?.firstChild); render(, holder); + return () => { + render(null, holder); + if (holder.parentElement) { + holder.parentElement.removeChild(holder); + } + }; } export default showWaveEffect; diff --git a/components/_util/wave/index.tsx b/components/_util/wave/index.tsx index 26ac1aff5e..26dab40f9c 100644 --- a/components/_util/wave/index.tsx +++ b/components/_util/wave/index.tsx @@ -33,13 +33,12 @@ export default defineComponent({ // =============================== Wave =============================== const showWave = useWave( - instance, computed(() => classNames(prefixCls.value, hashId.value)), wave, ); let onClick: (e: MouseEvent) => void; const clear = () => { - const node = findDOMNode(instance); + const node = findDOMNode(instance) as HTMLElement; node.removeEventListener('click', onClick, true); }; onMounted(() => { diff --git a/components/_util/wave/useWave.ts b/components/_util/wave/useWave.ts index 84e2a6effb..f88c361688 100644 --- a/components/_util/wave/useWave.ts +++ b/components/_util/wave/useWave.ts @@ -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 showWaveEffect from './WaveEffect'; export default function useWave( - instance: ComponentInternalInstance | null, className: Ref, wave?: ComputedRef<{ disabled?: boolean }>, ): VoidFunction { + const instance = getCurrentInstance(); + let stopWave: () => void; function showWave() { const node = findDOMNode(instance); - + stopWave?.(); if (wave?.value?.disabled || !node) { return; } - - showWaveEffect(node, className.value); + stopWave = showWaveEffect(node, className.value); } + onBeforeUnmount(() => { + stopWave?.(); + }); return showWave; } diff --git a/components/affix/index.tsx b/components/affix/index.tsx index a617febabb..8f4b37710f 100644 --- a/components/affix/index.tsx +++ b/components/affix/index.tsx @@ -176,7 +176,6 @@ const Affix = defineComponent({ affixStyle: undefined, placeholderStyle: undefined, }); - currentInstance.update(); // Test if `updatePosition` called if (process.env.NODE_ENV === 'test') { emit('testUpdatePosition'); @@ -256,7 +255,7 @@ const Affix = defineComponent({ const { prefixCls } = useConfigInject('affix', props); const [wrapSSR, hashId] = useStyle(prefixCls); return () => { - const { affixStyle, placeholderStyle } = state; + const { affixStyle, placeholderStyle, status } = state; const className = classNames({ [prefixCls.value]: affixStyle, [hashId.value]: true, @@ -271,7 +270,7 @@ const Affix = defineComponent({ ]); return wrapSSR( -
+
{affixStyle && diff --git a/components/input-number/style/index.tsx b/components/input-number/style/index.tsx index d119e7d663..e363f0772c 100644 --- a/components/input-number/style/index.tsx +++ b/components/input-number/style/index.tsx @@ -263,6 +263,10 @@ const genInputNumberStyles: GenerateStyle = (token: InputNumbe [`${componentCls}-handler-wrap`]: { display: 'none', }, + + [`${componentCls}-input`]: { + color: 'inherit', + }, }, [` diff --git a/components/input/Password.tsx b/components/input/Password.tsx index 0d65270b42..a516c0a84d 100644 --- a/components/input/Password.tsx +++ b/components/input/Password.tsx @@ -59,7 +59,7 @@ export default defineComponent({ }); const getIcon = (prefixCls: string) => { const { action, iconRender = slots.iconRender || defaultIconRender } = props; - const iconTrigger = ActionMap[action!] || ''; + const iconTrigger = ActionMap[action] || ''; const icon = iconRender(visible.value); const iconProps = { [iconTrigger]: onVisibleChange, diff --git a/components/input/ResizableTextArea.tsx b/components/input/ResizableTextArea.tsx index 862a7cb8a4..9ae5945173 100644 --- a/components/input/ResizableTextArea.tsx +++ b/components/input/ResizableTextArea.tsx @@ -1,4 +1,4 @@ -import type { VNode, CSSProperties } from 'vue'; +import type { CSSProperties } from 'vue'; import { computed, watchEffect, @@ -7,16 +7,16 @@ import { onBeforeUnmount, ref, defineComponent, - withDirectives, } from 'vue'; import ResizeObserver from '../vc-resize-observer'; import classNames from '../_util/classNames'; import raf from '../_util/raf'; import warning from '../_util/warning'; -import antInput from '../_util/antInputDirective'; import omit from '../_util/omit'; import { textAreaProps } from './inputProps'; import calculateAutoSizeStyle from './calculateNodeHeight'; +import type { BaseInputExpose } from '../_util/BaseInput'; +import BaseInput from '../_util/BaseInput'; const RESIZE_START = 0; const RESIZE_MEASURING = 1; @@ -30,7 +30,7 @@ const ResizableTextArea = defineComponent({ setup(props, { attrs, emit, expose }) { let nextFrameActionId: any; let resizeFrameId: any; - const textAreaRef = ref(); + const textAreaRef = ref(); const textareaStyles = ref({}); const resizeStatus = ref(RESIZE_STABLE); onBeforeUnmount(() => { @@ -41,12 +41,12 @@ const ResizableTextArea = defineComponent({ // https://github.com/ant-design/ant-design/issues/21870 const fixFirefoxAutoScroll = () => { try { - if (document.activeElement === textAreaRef.value) { - const currentStart = textAreaRef.value.selectionStart; - const currentEnd = textAreaRef.value.selectionEnd; - const scrollTop = textAreaRef.value.scrollTop; + if (textAreaRef.value && document.activeElement === textAreaRef.value.input) { + const currentStart = textAreaRef.value.getSelectionStart(); + const currentEnd = textAreaRef.value.getSelectionEnd(); + const scrollTop = textAreaRef.value.getScrollTop(); textAreaRef.value.setSelectionRange(currentStart, currentEnd); - textAreaRef.value.scrollTop = scrollTop; + textAreaRef.value.setScrollTop(scrollTop); } } catch (e) { // Fix error in Chrome: @@ -77,7 +77,7 @@ const ResizableTextArea = defineComponent({ startResize(); } }, - { immediate: true, flush: 'post' }, + { immediate: true }, ); const autoSizeStyle = ref(); watch( @@ -88,7 +88,7 @@ const ResizableTextArea = defineComponent({ resizeStatus.value = RESIZE_MEASURING; } else if (resizeStatus.value === RESIZE_MEASURING) { const textareaStyles = calculateAutoSizeStyle( - textAreaRef.value, + textAreaRef.value.input as HTMLTextAreaElement, false, minRows.value, maxRows.value, @@ -127,7 +127,7 @@ const ResizableTextArea = defineComponent({ expose({ resizeTextarea, - textArea: textAreaRef, + textArea: computed(() => textAreaRef.value?.input), instance, }); warning( @@ -146,7 +146,6 @@ const ResizableTextArea = defineComponent({ 'defaultValue', 'allowClear', 'type', - 'lazy', 'maxlength', 'valueModifiers', ]); @@ -175,9 +174,7 @@ const ResizableTextArea = defineComponent({ } return ( - {withDirectives((