Skip to content

Commit 527dec6

Browse files
committed
feat: select add status & placement
1 parent af9371f commit 527dec6

File tree

12 files changed

+226
-26
lines changed

12 files changed

+226
-26
lines changed

components/date-picker/demo/status.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<docs>
22
---
33
order: 19
4-
version: 4.19.0
4+
version: 3.3.0
55
title:
66
zh-CN: 自定义状态
77
en-US: Status

components/select/demo/index.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
<OptionLabelProp />
1919
<BigData />
2020
<fieldNamesVue />
21+
<placementVue />
22+
<statusVue />
2123
</demo-sort>
2224
</template>
2325
<script lang="ts">
@@ -39,13 +41,17 @@ import OptionLabelProp from './option-label-prop.vue';
3941
import BigData from './big-data.vue';
4042
import Responsive from './responsive.vue';
4143
import fieldNamesVue from './field-names.vue';
44+
import placementVue from './placement.vue';
45+
import statusVue from './status.vue';
4246
import CN from '../index.zh-CN.md';
4347
import US from '../index.en-US.md';
4448
import { defineComponent } from 'vue';
4549
export default defineComponent({
4650
CN,
4751
US,
4852
components: {
53+
placementVue,
54+
statusVue,
4955
fieldNamesVue,
5056
Basic,
5157
Size,

components/select/demo/placement.vue

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<docs>
2+
---
3+
order: 28
4+
title:
5+
zh-CN: 弹出位置
6+
en-US: Popup Placement
7+
---
8+
9+
## zh-CN
10+
11+
可以通过 `placement` 手动指定弹出的位置。
12+
13+
## en-US
14+
15+
You can manually specify the position of the popup via `placement`.
16+
17+
</docs>
18+
19+
<template>
20+
<a-radio-group v-model:value="placement">
21+
<a-radio-button value="topLeft">topLeft</a-radio-button>
22+
<a-radio-button value="topRight">topRight</a-radio-button>
23+
<a-radio-button value="bottomLeft">bottomLeft</a-radio-button>
24+
<a-radio-button value="bottomRight">bottomRight</a-radio-button>
25+
</a-radio-group>
26+
<br />
27+
<br />
28+
<a-select
29+
v-model:value="value"
30+
style="width: 120px"
31+
:dropdown-match-select-width="false"
32+
:placement="placement"
33+
>
34+
<a-select-option value="HangZhou">HangZhou #310000</a-select-option>
35+
<a-select-option value="NingBo">NingBo #315000</a-select-option>
36+
<a-select-option value="WenZhou">WenZhou #325000</a-select-option>
37+
</a-select>
38+
</template>
39+
<script lang="ts">
40+
import { defineComponent, ref } from 'vue';
41+
export default defineComponent({
42+
setup() {
43+
const placement = ref('topLeft' as const);
44+
return {
45+
placement,
46+
value: ref('HangZhou'),
47+
};
48+
},
49+
});
50+
</script>

components/select/demo/status.vue

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<docs>
2+
---
3+
order: 19
4+
version: 3.3.0
5+
title:
6+
zh-CN: 自定义状态
7+
en-US: Status
8+
---
9+
10+
## zh-CN
11+
12+
使用 `status` 为 DatePicker 添加状态,可选 `error` 或者 `warning`。
13+
14+
## en-US
15+
16+
Add status to DatePicker with `status`, which could be `error` or `warning`.
17+
18+
</docs>
19+
20+
<template>
21+
<a-space direction="vertical" style="width: 100%">
22+
<a-select status="error" style="width: 100%" />
23+
<a-select status="warning" style="width: 100%" />
24+
</a-space>
25+
</template>
26+
<script lang="ts">
27+
import { defineComponent } from 'vue';
28+
export default defineComponent({
29+
setup() {
30+
return {};
31+
},
32+
});
33+
</script>
34+
<style scoped>
35+
#components-select-demo-status .ant-select {
36+
margin: 0;
37+
}
38+
</style>

components/select/index.en-US.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,16 @@ Select component to select value from options.
5757
| optionLabelProp | Which prop value of option will render as content of select. | string | `children` \| `label`(when use options) | |
5858
| options | Data of the selectOption, manual construction work is no longer needed if this property has been set | array&lt;{value, label, [disabled, key, title]}> | \[] | |
5959
| placeholder | Placeholder of select | string\|slot | - | |
60+
| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | 3.3.0 |
6061
| removeIcon | The custom remove icon | VNode \| slot | - | |
6162
| searchValue | The current input "search" text | string | - | |
6263
| showArrow | Whether to show the drop-down arrow | boolean | true | |
6364
| showSearch | Whether show search input in single mode. | boolean | false | |
6465
| size | Size of Select input. `default` `large` `small` | string | default | |
66+
| status | Set validation status | 'error' \| 'warning' | - | 3.3.0 |
6567
| suffixIcon | The custom suffix icon | VNode \| slot | - | |
6668
| tagRender | Customize tag render, only applies when `mode` is set to `multiple` or `tags` | slot \| (props) => any | - | |
67-
| tokenSeparators | Separator used to tokenize on tag/multiple mode | string\[] | | |
69+
| tokenSeparators | Separator used to tokenize, only applies when `mode="tags"` | string\[] | - | |
6870
| value(v-model) | Current selected option. | string\|number\|string\[]\|number\[] | - | |
6971
| virtual | Disable virtual scroll when set to false | boolean | true | 3.0 |
7072

@@ -114,7 +116,7 @@ Select component to select value from options.
114116

115117
### The dropdown is closed when click `dropdownRender` area?
116118

117-
See the [dropdownRender example](/components/select/#components-select-demo-custom-dropdown).
119+
Dropdown menu will be closed if click `dropdownRender` area, you can prevent it by wrapping `@mousedown.prevent` See the [dropdownRender example](/components/select/#components-select-demo-custom-dropdown).
118120

119121
### Why is `placeholder` not displayed?
120122

components/select/index.tsx

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ import getIcons from './utils/iconUtil';
99
import PropTypes from '../_util/vue-types';
1010
import useConfigInject from '../_util/hooks/useConfigInject';
1111
import omit from '../_util/omit';
12-
import { useInjectFormItemContext } from '../form/FormItemContext';
13-
import { getTransitionName } from '../_util/transition';
12+
import { FormItemInputContext, useInjectFormItemContext } from '../form/FormItemContext';
13+
import type { SelectCommonPlacement } from '../_util/transition';
14+
import { getTransitionDirection, getTransitionName } from '../_util/transition';
1415
import type { SizeType } from '../config-provider';
1516
import { initDefaultProps } from '../_util/props-util';
17+
import type { InputStatus } from '../_util/statusUtils';
18+
import { getStatusClassNames, getMergedStatus } from '../_util/statusUtils';
1619

1720
type RawValue = string | number;
1821

@@ -48,6 +51,8 @@ export const selectProps = () => ({
4851
bordered: { type: Boolean, default: true },
4952
transitionName: String,
5053
choiceTransitionName: { type: String, default: '' },
54+
placement: String as PropType<SelectCommonPlacement>,
55+
status: String as PropType<InputStatus>,
5156
'onUpdate:value': Function as PropType<(val: SelectValue) => void>,
5257
});
5358

@@ -81,6 +86,8 @@ const Select = defineComponent({
8186
setup(props, { attrs, emit, slots, expose }) {
8287
const selectRef = ref<BaseSelectRef>();
8388
const formItemContext = useInjectFormItemContext();
89+
const formItemInputContext = FormItemInputContext.useInject();
90+
const mergedStatus = computed(() => getMergedStatus(formItemInputContext.status, props.status));
8491
const focus = () => {
8592
selectRef.value?.focus();
8693
};
@@ -111,16 +118,33 @@ const Select = defineComponent({
111118
props,
112119
);
113120
const rootPrefixCls = computed(() => getPrefixCls());
121+
// ===================== Placement =====================
122+
const placement = computed(() => {
123+
if (props.placement !== undefined) {
124+
return props.placement;
125+
}
126+
return direction.value === 'rtl'
127+
? ('bottomRight' as SelectCommonPlacement)
128+
: ('bottomLeft' as SelectCommonPlacement);
129+
});
114130
const transitionName = computed(() =>
115-
getTransitionName(rootPrefixCls.value, 'slide-up', props.transitionName),
131+
getTransitionName(
132+
rootPrefixCls.value,
133+
getTransitionDirection(placement.value),
134+
props.transitionName,
135+
),
116136
);
117137
const mergedClassName = computed(() =>
118-
classNames({
119-
[`${prefixCls.value}-lg`]: size.value === 'large',
120-
[`${prefixCls.value}-sm`]: size.value === 'small',
121-
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
122-
[`${prefixCls.value}-borderless`]: !props.bordered,
123-
}),
138+
classNames(
139+
{
140+
[`${prefixCls.value}-lg`]: size.value === 'large',
141+
[`${prefixCls.value}-sm`]: size.value === 'small',
142+
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
143+
[`${prefixCls.value}-borderless`]: !props.bordered,
144+
[`${prefixCls.value}-in-form-item`]: formItemInputContext.isFormItemInput,
145+
},
146+
getStatusClassNames(prefixCls.value, mergedStatus.value, formItemInputContext.hasFeedback),
147+
),
124148
);
125149
const triggerChange: SelectProps['onChange'] = (...args) => {
126150
emit('update:value', args[0]);
@@ -137,6 +161,12 @@ const Select = defineComponent({
137161
scrollTo,
138162
});
139163
const isMultiple = computed(() => mode.value === 'multiple' || mode.value === 'tags');
164+
const mergedShowArrow = computed(() =>
165+
props.showArrow !== undefined
166+
? props.showArrow
167+
: props.loading || !(isMultiple.value || mode.value === 'combobox'),
168+
);
169+
140170
return () => {
141171
const {
142172
notFoundContent,
@@ -148,8 +178,9 @@ const Select = defineComponent({
148178
dropdownMatchSelectWidth,
149179
id = formItemContext.id.value,
150180
placeholder = slots.placeholder?.(),
181+
showArrow,
151182
} = props;
152-
183+
const { hasFeedback, feedbackIcon } = formItemInputContext;
153184
const { renderEmpty, getPopupContainer: getContextPopupContainer } = configProvider;
154185

155186
// ===================== Empty =====================
@@ -170,6 +201,9 @@ const Select = defineComponent({
170201
...props,
171202
multiple: isMultiple.value,
172203
prefixCls: prefixCls.value,
204+
hasFeedback,
205+
feedbackIcon,
206+
showArrow: mergedShowArrow.value,
173207
},
174208
slots,
175209
);
@@ -182,9 +216,10 @@ const Select = defineComponent({
182216
'clearIcon',
183217
'size',
184218
'bordered',
219+
'status',
185220
]);
186221

187-
const rcSelectRtlDropDownClassName = classNames(dropdownClassName, {
222+
const rcSelectRtlDropdownClassName = classNames(dropdownClassName, {
188223
[`${prefixCls.value}-dropdown-${direction.value}`]: direction.value === 'rtl',
189224
});
190225
return (
@@ -207,7 +242,7 @@ const Select = defineComponent({
207242
notFoundContent={mergedNotFound}
208243
class={[mergedClassName.value, attrs.class]}
209244
getPopupContainer={getPopupContainer || getContextPopupContainer}
210-
dropdownClassName={rcSelectRtlDropDownClassName}
245+
dropdownClassName={rcSelectRtlDropdownClassName}
211246
onChange={triggerChange}
212247
onBlur={handleBlur}
213248
id={id}
@@ -218,6 +253,7 @@ const Select = defineComponent({
218253
tagRender={props.tagRender || slots.tagRender}
219254
optionLabelRender={slots.optionLabel}
220255
maxTagPlaceholder={props.maxTagPlaceholder || slots.maxTagPlaceholder}
256+
showArrow={hasFeedback || showArrow}
221257
></RcSelect>
222258
);
223259
};

components/select/index.zh-CN.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,16 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
5757
| optionLabelProp | 回填到选择框的 Option 的属性值,默认是 Option 的子元素。比如在子元素需要高亮效果时,此值可以设为 `value`| string | `children` \| `label`(设置 options 时) | |
5858
| options | options 数据,如果设置则不需要手动构造 selectOption 节点 | array&lt;{value, label, [disabled, key, title]}> | \[] | |
5959
| placeholder | 选择框默认文字 | string\|slot | - | |
60+
| placement | 选择框弹出的位置 | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | 3.3.0 |
6061
| removeIcon | 自定义的多选框清除图标 | VNode \| slot | - | |
6162
| searchValue | 控制搜索文本 | string | - | |
6263
| showArrow | 是否显示下拉小箭头 | boolean | true | |
6364
| showSearch | 使单选模式可搜索 | boolean | false | |
6465
| size | 选择框大小,可选 `large` `small` | string | default | |
66+
| status | 设置校验状态 | 'error' \| 'warning' | - | 3.3.0 |
6567
| suffixIcon | 自定义的选择框后缀图标 | VNode \| slot | - | |
6668
| tagRender | 自定义 tag 内容 render,仅在 `mode``multiple``tags` 时生效 | slot \| (props) => any | - | 3.0 |
67-
| tokenSeparators | tags 和 multiple 模式下自动分词的分隔符 | string\[] | | |
69+
| tokenSeparators | 自动分词的分隔符,仅在 `mode="tags"` 时生效 | string\[] | - | |
6870
| value(v-model) | 指定当前选中的条目 | string\|string\[]\|number\|number\[] | - | |
6971
| virtual | 设置 false 时关闭虚拟滚动 | boolean | true | 3.0 |
7072

@@ -114,7 +116,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_0XzgOis7/Select.svg
114116

115117
### 点击 `dropdownRender` 里的内容浮层关闭怎么办?
116118

117-
看下 [dropdownRender 例子](/components/select-cn/#components-select-demo-custom-dropdown) 里的说明。
119+
自定义内容点击时会关闭浮层,如果不喜欢关闭,可以添加 `@mousedown.prevent` 进行阻止。 看下 [dropdownRender 例子](/components/select-cn/#components-select-demo-custom-dropdown) 里的说明。
118120

119121
### 为什么 `placeholder` 不显示 ?
120122

components/select/style/index.less

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
@import '../../input/style/mixin';
44
@import './single';
55
@import './multiple';
6+
@import './status';
67

78
@select-prefix-cls: ~'@{ant-prefix}-select';
89
@select-height-without-border: @input-height-base - 2 * @border-width-base;
@@ -12,7 +13,7 @@
1213
position: relative;
1314
background-color: @select-background;
1415
border: @border-width-base @border-style-base @select-border-color;
15-
border-radius: @border-radius-base;
16+
border-radius: @control-border-radius;
1617
transition: all 0.3s @ease-in-out;
1718

1819
input {
@@ -120,7 +121,8 @@
120121
position: absolute;
121122
top: 50%;
122123
right: @control-padding-horizontal - 1px;
123-
width: @font-size-sm;
124+
display: flex;
125+
align-items: center;
124126
height: @font-size-sm;
125127
margin-top: (-@font-size-sm / 2);
126128
color: @disabled-color;
@@ -145,6 +147,10 @@
145147
.@{select-prefix-cls}-disabled & {
146148
cursor: not-allowed;
147149
}
150+
151+
> *:not(:last-child) {
152+
margin-inline-end: @padding-xs;
153+
}
148154
}
149155

150156
// ========================== Clear ==========================
@@ -315,6 +321,10 @@
315321
border-color: transparent !important;
316322
box-shadow: none !important;
317323
}
324+
325+
&&-in-form-item {
326+
width: 100%;
327+
}
318328
}
319329

320330
@import './rtl';

components/select/style/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ import './index.less';
33

44
// style dependencies
55
import '../../empty/style';
6+
7+
// deps-lint-skip: form

components/select/style/multiple.less

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@
110110
cursor: pointer;
111111

112112
> .@{iconfont-css-prefix} {
113-
vertical-align: -0.2em;
113+
vertical-align: middle;
114114
}
115115

116116
&:hover {

0 commit comments

Comments
 (0)