Merge branch 'vueComponent:main' into main
commit
e49f063866
|
@ -10,6 +10,13 @@
|
|||
|
||||
---
|
||||
|
||||
## 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
|
||||
|
|
|
@ -10,6 +10,13 @@
|
|||
|
||||
---
|
||||
|
||||
## 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 组件内存泄漏问题
|
||||
|
|
|
@ -122,6 +122,8 @@ See [iconfont.cn documents](http://iconfont.cn/help/detail?spm=a313x.7781069.199
|
|||
|
||||
### 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).
|
||||
|
||||
```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:
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
|
|
|
@ -119,7 +119,9 @@ export default defineComponent({
|
|||
|
||||
### 自定义 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
|
||||
// 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` 组件的接受的属性如下:
|
||||
|
||||
| 字段 | 说明 | 类型 | 只读值 |
|
||||
|
|
|
@ -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 { which } = event;
|
||||
userTypingRef.value = true;
|
||||
|
@ -577,6 +582,7 @@ export default defineComponent({
|
|||
onBlur={onBlur}
|
||||
onCompositionstart={onCompositionStart}
|
||||
onCompositionend={onCompositionEnd}
|
||||
onBeforeinput={onBeforeInput}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -263,6 +263,10 @@ const genInputNumberStyles: GenerateStyle<InputNumberToken> = (token: InputNumbe
|
|||
[`${componentCls}-handler-wrap`]: {
|
||||
display: 'none',
|
||||
},
|
||||
|
||||
[`${componentCls}-input`]: {
|
||||
color: 'inherit',
|
||||
},
|
||||
},
|
||||
|
||||
[`
|
||||
|
|
|
@ -159,6 +159,52 @@ describe('Select', () => {
|
|||
}, 500);
|
||||
});
|
||||
|
||||
it('The select trigger should be blur when the panel is closed.', async () => {
|
||||
const wrapper = mount(
|
||||
{
|
||||
render() {
|
||||
return (
|
||||
<Select
|
||||
dropdownRender={() => {
|
||||
return <input id="dropdownRenderInput" />;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
sync: false,
|
||||
attachTo: 'body',
|
||||
},
|
||||
);
|
||||
await asyncExpect(async () => {
|
||||
await wrapper.find('.ant-select-selector').trigger('mousedown');
|
||||
await wrapper.find('.ant-select-selection-search-input').trigger('focus');
|
||||
});
|
||||
|
||||
await asyncExpect(async () => {
|
||||
const el = wrapper.find('.ant-select');
|
||||
|
||||
expect(el.classes()).toContain('ant-select-focused');
|
||||
$$('#dropdownRenderInput')[0].focus();
|
||||
|
||||
expect(el.classes()).toContain('ant-select-focused');
|
||||
|
||||
document.body.dispatchEvent(
|
||||
new MouseEvent('mousedown', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
view: window,
|
||||
}),
|
||||
);
|
||||
}, 100);
|
||||
|
||||
await asyncExpect(async () => {
|
||||
const el = wrapper.find('.ant-select');
|
||||
expect(el.classes()).not.toContain('ant-select-focused');
|
||||
}, 200);
|
||||
});
|
||||
|
||||
describe('Select Custom Icons', () => {
|
||||
it('should support customized icons', () => {
|
||||
const wrapper = mount({
|
||||
|
|
|
@ -63,7 +63,7 @@ Select component to select value from options.
|
|||
| searchValue | The current input "search" text | string | - | |
|
||||
| showArrow | Whether to show the drop-down arrow | boolean | single:true, multiple:false | |
|
||||
| showSearch | Whether select is searchable | boolean | single:false, multiple:true | |
|
||||
| size | Size of Select input. `default` `large` `small` | string | default | |
|
||||
| size | Size of Select input. `middle` `large` `small` | string | middle | |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 3.3.0 |
|
||||
| suffixIcon | The custom suffix icon | VNode \| slot | - | |
|
||||
| tagRender | Customize tag render, only applies when `mode` is set to `multiple` or `tags` | slot \| (props) => any | - | |
|
||||
|
|
|
@ -63,7 +63,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*5oPiTqPxGAUAAA
|
|||
| searchValue | 控制搜索文本 | string | - | |
|
||||
| showArrow | 是否显示下拉小箭头 | boolean | 单选为 true,多选为 false | |
|
||||
| showSearch | 配置是否可搜索 | boolean | 单选为 false,多选为 true | |
|
||||
| size | 选择框大小,可选 `large` `small` | string | default | |
|
||||
| size | 选择框大小,可选 `middle` `large` `small` | string | middle | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 3.3.0 |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | VNode \| slot | - | |
|
||||
| tagRender | 自定义 tag 内容 render,仅在 `mode` 为 `multiple` 或 `tags` 时生效 | slot \| (props) => any | - | 3.0 |
|
||||
|
|
|
@ -24,6 +24,7 @@ Ant Design has 3 types of Tabs for different situations.
|
|||
| --- | --- | --- | --- | --- |
|
||||
| activeKey(v-model) | Current TabPane's key | string | - | |
|
||||
| animated | Whether to change tabs with animation. Only works while tabPosition=`"top"` \| `"bottom"` | boolean \| {inkBar:boolean, tabPane:boolean} | `true`, `false` when `type="card"` | |
|
||||
| centered | Whether to display the labels in the center | boolean | false | 3.0 |
|
||||
| destroyInactiveTabPane | Whether destroy inactive TabPane when change tab | boolean | false | |
|
||||
| hideAdd | Hide plus icon or not. Only works while `type="editable-card"` | boolean | `false` | } |
|
||||
| size | preset tab bar size | `large` \| `middle` \| `small` | `middle` | |
|
||||
|
|
|
@ -74,7 +74,7 @@ Almost anything can be represented in a tree structure. Examples include directo
|
|||
| disableCheckbox | Disables the checkbox of the treeNode | boolean | false | |
|
||||
| disabled | Disables the treeNode | boolean | false | |
|
||||
| icon | customize icon. When you pass component, whose render will receive full TreeNode props as component props | slot\|slot-scope | - | |
|
||||
| isLeaf | Determines if this is a leaf node(effective when `loadData` is specified) | boolean | false | |
|
||||
| isLeaf | Determines if this is a leaf node(effective when `loadData` is specified) | boolean | - | |
|
||||
| key | Used with (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys. P.S.: It must be unique in all of treeNodes of the tree! | string \| number | internal calculated position of treeNode | |
|
||||
| selectable | Set whether the treeNode can be selected | boolean | true | |
|
||||
| style | style | string\|object | - | |
|
||||
|
|
|
@ -75,7 +75,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*1GeUQJPTGUYAAA
|
|||
| disableCheckbox | 禁掉 checkbox | boolean | false | |
|
||||
| disabled | 禁掉响应 | boolean | false | |
|
||||
| icon | 自定义图标。可接收组件,props 为当前节点 props | slot\|slot-scope | - | |
|
||||
| isLeaf | 设置为叶子节点(设置了`loadData`时有效) | boolean | false | |
|
||||
| isLeaf | 设置为叶子节点(设置了`loadData`时有效) | boolean | - | |
|
||||
| key | 被树的 (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys 属性所用。注意:整个树范围内的所有节点的 key 值不能重复! | string \| number | 内部计算出的节点位置 | |
|
||||
| selectable | 设置节点是否可被选中 | boolean | true | |
|
||||
| style | 节点的 style | string\|object | - | |
|
||||
|
|
|
@ -5,7 +5,7 @@ import { getTransitionProps } from '../_util/transition';
|
|||
import dialogPropTypes from './IDialogPropTypes';
|
||||
import { offset } from './util';
|
||||
const sentinelStyle = { width: 0, height: 0, overflow: 'hidden', outline: 'none' };
|
||||
|
||||
const entityStyle = { outline: 'none' };
|
||||
export type ContentRef = {
|
||||
focus: () => void;
|
||||
changeActive: (next: boolean) => void;
|
||||
|
@ -28,14 +28,14 @@ export default defineComponent({
|
|||
const dialogRef = ref<HTMLDivElement>();
|
||||
expose({
|
||||
focus: () => {
|
||||
sentinelStartRef.value?.focus();
|
||||
sentinelStartRef.value?.focus({ preventScroll: true });
|
||||
},
|
||||
changeActive: next => {
|
||||
const { activeElement } = document;
|
||||
if (next && activeElement === sentinelEndRef.value) {
|
||||
sentinelStartRef.value.focus();
|
||||
sentinelStartRef.value.focus({ preventScroll: true });
|
||||
} else if (!next && activeElement === sentinelStartRef.value) {
|
||||
sentinelEndRef.value.focus();
|
||||
sentinelEndRef.value.focus({ preventScroll: true });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -143,9 +143,10 @@ export default defineComponent({
|
|||
onMousedown={onMousedown}
|
||||
onMouseup={onMouseup}
|
||||
>
|
||||
<div tabindex={0} ref={sentinelStartRef} style={sentinelStyle} aria-hidden="true" />
|
||||
{modalRender ? modalRender({ originVNode: content }) : content}
|
||||
<div tabindex={0} ref={sentinelEndRef} style={sentinelStyle} aria-hidden="true" />
|
||||
<div tabindex={0} ref={sentinelStartRef} style={entityStyle}>
|
||||
{modalRender ? modalRender({ originVNode: content }) : content}
|
||||
</div>
|
||||
<div tabindex={0} ref={sentinelEndRef} style={sentinelStyle} />
|
||||
</div>
|
||||
) : null}
|
||||
</Transition>
|
||||
|
|
|
@ -343,6 +343,14 @@ export default defineComponent({
|
|||
if (mergedOpen.value !== nextOpen && !props.disabled) {
|
||||
setInnerOpen(nextOpen);
|
||||
props.onDropdownVisibleChange && props.onDropdownVisibleChange(nextOpen);
|
||||
|
||||
if (!nextOpen && popupFocused.value) {
|
||||
popupFocused.value = false;
|
||||
setMockFocused(false, () => {
|
||||
focusRef.value = false;
|
||||
blurRef.value = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -196,7 +196,7 @@ export default defineComponent({
|
|||
ref={alignRef}
|
||||
monitorWindowResize
|
||||
disabled={alignDisabled.value}
|
||||
align={align}
|
||||
align={align as any}
|
||||
onAlign={onInternalAlign}
|
||||
v-slots={{
|
||||
default: () => (
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ant-design-vue",
|
||||
"version": "4.2.5",
|
||||
"version": "4.2.6",
|
||||
"title": "Ant Design Vue",
|
||||
"description": "An enterprise-class UI design language and Vue-based implementation",
|
||||
"keywords": [
|
||||
|
|
|
@ -2,10 +2,11 @@ import type { InputProps } from 'ant-design-vue';
|
|||
import { ConfigProvider, Input, InputNumber, Select, theme } from 'ant-design-vue';
|
||||
import classNames from 'ant-design-vue/es/_util/classNames';
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent, watchEffect, watch, computed, toRefs, ref } from 'vue';
|
||||
import { defineComponent, watchEffect, computed, toRefs, ref } from 'vue';
|
||||
import { HexColorPicker, RgbaColorPicker } from '../vue-colorful';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import makeStyle from './utils/makeStyle';
|
||||
import type { ValueType } from 'ant-design-vue/es/input-number/src/utils/MiniDecimal';
|
||||
|
||||
const { useToken } = theme;
|
||||
|
||||
|
@ -168,24 +169,42 @@ const RgbColorInput = defineComponent({
|
|||
setup(props) {
|
||||
const { value, alpha } = toRefs(props);
|
||||
|
||||
watch(value, val => {
|
||||
props.onChange(val);
|
||||
});
|
||||
|
||||
const handleChange = (val: ValueType, key: 'r' | 'g' | 'b' | 'a') => {
|
||||
value.value[key] = val;
|
||||
props.onChange(value.value);
|
||||
};
|
||||
return () => {
|
||||
return (
|
||||
<div class="color-panel-rgba-input">
|
||||
<ConfigProvider theme={{ components: { InputNumber: { handleWidth: 12 } } }}>
|
||||
<div class="color-panel-rgba-input-part">
|
||||
<InputNumber min={0} max={255} size="small" v-model={[value.value.r, 'value']} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={255}
|
||||
size="small"
|
||||
value={value.value.r}
|
||||
onChange={val => handleChange(val, 'r')}
|
||||
/>
|
||||
<div class="color-panel-mode-title">R</div>
|
||||
</div>
|
||||
<div class="color-panel-rgba-input-part">
|
||||
<InputNumber min={0} max={255} size="small" v-model={[value.value.g, 'value']} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={255}
|
||||
size="small"
|
||||
value={value.value.g}
|
||||
onChange={val => handleChange(val, 'g')}
|
||||
/>
|
||||
<div class="color-panel-mode-title">G</div>
|
||||
</div>
|
||||
<div class="color-panel-rgba-input-part">
|
||||
<InputNumber min={0} max={255} size="small" v-model={[value.value.b, 'value']} />
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={255}
|
||||
size="small"
|
||||
value={value.value.b}
|
||||
onChange={val => handleChange(val, 'b')}
|
||||
/>
|
||||
<div class="color-panel-mode-title">B</div>
|
||||
</div>
|
||||
{alpha.value && (
|
||||
|
@ -195,7 +214,8 @@ const RgbColorInput = defineComponent({
|
|||
max={1}
|
||||
step={0.01}
|
||||
size="small"
|
||||
v-model={[value.value.a, 'value']}
|
||||
value={value.value.a}
|
||||
onChange={val => handleChange(val, 'a')}
|
||||
/>
|
||||
<div class="color-panel-mode-title">A</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue