@@ -216,17 +252,21 @@ exports[`renders ./components/select/demo/size.md correctly 1`] = `
exports[`renders ./components/select/demo/suffix.md correctly 1`] = `
@@ -234,9 +274,11 @@ exports[`renders ./components/select/demo/suffix.md correctly 1`] = `
exports[`renders ./components/select/demo/tags.md correctly 1`] = `
-
+
-
Tags Mode
+
Tags Mode
diff --git a/components/select/__tests__/__snapshots__/index.test.js.snap b/components/select/__tests__/__snapshots__/index.test.js.snap
new file mode 100644
index 000000000..173cf59e7
--- /dev/null
+++ b/components/select/__tests__/__snapshots__/index.test.js.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Select Select Custom Icons should support customized icons 1`] = `
+
+`;
diff --git a/components/select/__tests__/index.test.js b/components/select/__tests__/index.test.js
index 1ba698512..af1146a13 100644
--- a/components/select/__tests__/index.test.js
+++ b/components/select/__tests__/index.test.js
@@ -1,6 +1,7 @@
import { mount } from '@vue/test-utils'
import { asyncExpect } from '@/tests/utils'
import Select from '..'
+import Icon from '../../icon'
import focusTest from '../../../tests/shared/focusTest'
describe('Select', () => {
@@ -144,4 +145,23 @@ describe('Select', () => {
expect(triggerComponent.componentInstance.visible).toBe(false)
})
})
+
+ describe('Select Custom Icons', () => {
+ it('should support customized icons', () => {
+ const wrapper = mount({
+ render () {
+ return (
+ }
+ clearIcon={}
+ menuItemSelectedIcon={}
+ >
+
+
+ )
+ },
+ })
+ expect(wrapper.html()).toMatchSnapshot()
+ })
+ })
})
diff --git a/components/select/demo/basic.md b/components/select/demo/basic.md
index 93ce02ada..470cc5f2a 100644
--- a/components/select/demo/basic.md
+++ b/components/select/demo/basic.md
@@ -21,6 +21,9 @@ Basic Usage
Lucy
+
+ Lucy
+
+```
+
diff --git a/components/select/demo/hide-selected.md b/components/select/demo/hide-selected.md
new file mode 100644
index 000000000..ecfdeecb9
--- /dev/null
+++ b/components/select/demo/hide-selected.md
@@ -0,0 +1,47 @@
+
+
+#### 隐藏已选择选项
+隐藏下拉列表中已选择的选项。
+
+
+
+#### Hide Already Selected
+Hide already selected options in the dropdown.
+
+
+```html
+
+
+
+ {{item}}
+
+
+
+
+```
+
diff --git a/components/select/demo/index.vue b/components/select/demo/index.vue
index 7245cfaaa..ba3522329 100644
--- a/components/select/demo/index.vue
+++ b/components/select/demo/index.vue
@@ -11,6 +11,8 @@ import SearchBox from './search-box'
import Search from './search'
import SelectUsers from './select-users'
import Suffix from './suffix'
+import HideSelected from './hide-selected'
+import CustomDropdownMenu from './custom-dropdown-menu'
import CN from '../index.zh-CN.md'
import US from '../index.en-US.md'
@@ -50,6 +52,8 @@ export default {
+
+
diff --git a/components/select/index.en-US.md b/components/select/index.en-US.md
index f61812b30..e46dbbf6a 100644
--- a/components/select/index.en-US.md
+++ b/components/select/index.en-US.md
@@ -19,6 +19,7 @@
| disabled | Whether disabled select | boolean | false |
| dropdownClassName | className of dropdown menu | string | - |
| dropdownMatchSelectWidth | Whether dropdown's width is same with select. | boolean | true |
+| dropdownRender | Customize dropdown content | (menuNode: VNode, props) => VNode | - |
| dropdownStyle | style of dropdown menu | object | - |
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded. | boolean or function(inputValue, option) | true |
| firstActiveValue | Value of action option by default | string\|string\[] | - |
@@ -34,12 +35,16 @@
| showSearch | Whether show search input in single mode. | boolean | false |
| showArrow | Whether to show the drop-down arrow | boolean | true |
| size | Size of Select input. `default` `large` `small` | string | default |
-| suffixIcon | The custom suffix icon | string \| VNode \| slot | - |
+| suffixIcon | The custom suffix icon | VNode \| slot | - |
+| removeIcon | The custom remove icon | VNode \| slot | - |
+| clearIcon | The custom clear icon | VNode \| slot | - |
+| menuItemSelectedIcon | The custom menuItemSelected icon | VNode \| slot | - |
| tokenSeparators | Separator used to tokenize on tag/multiple mode | string\[] | |
| value(v-model) | Current selected option. | string\|number\|string\[]\|number\[] | - |
| options | Data of the selectOption, manual construction work is no longer needed if this property has been set | array<{value, label, [disabled, key, title]}> | \[] |
| defaultOpen | Initial open state of dropdown | boolean | - |
| open | Controlled open state of dropdown | boolean | - |
+| loading | indicate loading state | Boolean | false |
### events
| Events Name | Description | Arguments |
diff --git a/components/select/index.jsx b/components/select/index.jsx
index 7926eec9f..a1306424b 100644
--- a/components/select/index.jsx
+++ b/components/select/index.jsx
@@ -12,6 +12,10 @@ import { cloneElement } from '../_util/vnode'
const AbstractSelectProps = () => ({
prefixCls: PropTypes.string,
size: PropTypes.oneOf(['small', 'large', 'default']),
+ showAction: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.arrayOf(String),
+ ]),
notFoundContent: PropTypes.any,
transitionName: PropTypes.string,
choiceTransitionName: PropTypes.string,
@@ -37,6 +41,8 @@ const AbstractSelectProps = () => ({
open: PropTypes.bool,
defaultOpen: PropTypes.bool,
autoClearSearchValue: PropTypes.bool,
+ dropdownRender: PropTypes.func,
+ loading: PropTypes.bool,
})
const Value = PropTypes.shape({
key: PropTypes.string,
@@ -71,6 +77,9 @@ const SelectProps = {
getInputElement: PropTypes.func,
options: PropTypes.array,
suffixIcon: PropTypes.any,
+ removeIcon: PropTypes.any,
+ clearIcon: PropTypes.any,
+ menuItemSelectedIcon: PropTypes.any,
}
const SelectPropTypes = {
@@ -107,6 +116,9 @@ const Select = {
prop: 'value',
event: 'change',
},
+ inject: {
+ configProvider: { default: {}},
+ },
created () {
warning(
this.$props.mode !== 'combobox',
@@ -134,17 +146,39 @@ const Select = {
const { mode } = this
return mode === 'combobox' || mode === SECRET_COMBOBOX_MODE_DO_NOT_USE
},
+
+ renderSuffixIcon () {
+ const { prefixCls, loading } = this.$props
+ let suffixIcon = getComponentFromProp(this, 'suffixIcon')
+ suffixIcon = Array.isArray(suffixIcon) ? suffixIcon[0] : suffixIcon
+ if (suffixIcon) {
+ return isValidElement(suffixIcon)
+ ? cloneElement(suffixIcon, { class: `${prefixCls}-arrow-icon` })
+ : suffixIcon
+ }
+ if (loading) {
+ return
+ }
+ return
+ },
+
renderSelect (locale) {
const {
prefixCls,
size,
mode,
options,
+ getPopupContainer,
...restProps
} = getOptionProps(this)
- let suffixIcon = getComponentFromProp(this, 'suffixIcon')
- suffixIcon = Array.isArray(suffixIcon) ? suffixIcon[0] : suffixIcon
- const rest = omit(restProps, ['inputIcon', 'removeIcon', 'clearIcon', 'suffixIcon'])
+ const { getPopupContainer: getContextPopupContainer } = this.configProvider
+ let removeIcon = getComponentFromProp(this, 'removeIcon')
+ removeIcon = Array.isArray(removeIcon) ? removeIcon[0] : removeIcon
+ let clearIcon = getComponentFromProp(this, 'clearIcon')
+ clearIcon = Array.isArray(clearIcon) ? clearIcon[0] : clearIcon
+ let menuItemSelectedIcon = getComponentFromProp(this, 'menuItemSelectedIcon')
+ menuItemSelectedIcon = Array.isArray(menuItemSelectedIcon) ? menuItemSelectedIcon[0] : menuItemSelectedIcon
+ const rest = omit(restProps, ['inputIcon', 'removeIcon', 'clearIcon', 'suffixIcon', 'menuItemSelectedIcon'])
const cls = {
[`${prefixCls}-lg`]: size === 'large',
@@ -162,31 +196,27 @@ const Select = {
tags: mode === 'tags',
combobox: this.isCombobox(),
}
+ const finalRemoveIcon = (removeIcon &&
+ (isValidElement(removeIcon)
+ ? cloneElement(removeIcon, { class: `${prefixCls}-remove-icon` })
+ : removeIcon)) ||
- const inputIcon = suffixIcon && (
- isValidElement(suffixIcon)
- ? cloneElement(suffixIcon) : suffixIcon) || (
-
- )
+ const finalClearIcon = (clearIcon &&
+ (isValidElement(clearIcon)
+ ? cloneElement(clearIcon, { class: `${prefixCls}-clear-icon` })
+ : clearIcon)) || ()
- const removeIcon = (
-
- )
-
- const clearIcon = (
-
- )
-
- const menuItemSelectedIcon = (
-
- )
+ const finalMenuItemSelectedIcon = (menuItemSelectedIcon &&
+ (isValidElement(menuItemSelectedIcon)
+ ? cloneElement(menuItemSelectedIcon, { class: `${prefixCls}-selected-icon` })
+ : menuItemSelectedIcon)) ||
const selectProps = {
props: {
- inputIcon,
- removeIcon,
- clearIcon,
- menuItemSelectedIcon,
+ inputIcon: this.renderSuffixIcon(),
+ removeIcon: finalRemoveIcon,
+ clearIcon: finalClearIcon,
+ menuItemSelectedIcon: finalMenuItemSelectedIcon,
...rest,
...modeConfig,
prefixCls,
@@ -201,6 +231,8 @@ const Select = {
})
: filterEmpty(this.$slots.default),
__propsSymbol__: Symbol(),
+ dropdownRender: getComponentFromProp(this, 'dropdownRender', {}, false),
+ getPopupContainer: getPopupContainer || getContextPopupContainer,
},
on: this.$listeners,
class: cls,
diff --git a/components/select/index.zh-CN.md b/components/select/index.zh-CN.md
index ef25c7211..fea1f66df 100644
--- a/components/select/index.zh-CN.md
+++ b/components/select/index.zh-CN.md
@@ -18,6 +18,7 @@
| disabled | 是否禁用 | boolean | false |
| dropdownClassName | 下拉菜单的 className 属性 | string | - |
| dropdownMatchSelectWidth | 下拉菜单和选择器同宽 | boolean | true |
+| dropdownRender | 自定义下拉框内容 | (menuNode: VNode, props) => VNode | - |
| dropdownStyle | 下拉菜单的 style 属性 | object | - |
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。 | boolean or function(inputValue, option) | true |
| firstActiveValue | 默认高亮的选项 | string\|string\[] | - |
@@ -33,7 +34,10 @@
| showSearch | 使单选模式可搜索 | boolean | false |
| showArrow | 是否显示下拉小箭头 | boolean | true |
| size | 选择框大小,可选 `large` `small` | string | default |
-| suffixIcon | 自定义的选择框后缀图标 | string \| VNode \| slot | - |
+| suffixIcon | 自定义的选择框后缀图标 | VNode \| slot | - |
+| removeIcon | 自定义的多选框清除图标 | VNode \| slot | - |
+| clearIcon | 自定义的多选框清空图标 | VNode \| slot | - |
+| menuItemSelectedIcon | 自定义当前选中的条目图标 | VNode \| slot | - |
| tokenSeparators | 在 tags 和 multiple 模式下自动分词的分隔符 | string\[] | |
| value(v-model) | 指定当前选中的条目 | string\|string\[]\|number\|number\[] | - |
| options | options 数据,如果设置则不需要手动构造 selectOption 节点 | array<{value, label, [disabled, key, title]}> | \[] |
diff --git a/components/vc-select/Select.jsx b/components/vc-select/Select.jsx
index a443d28fa..4514fea05 100644
--- a/components/vc-select/Select.jsx
+++ b/components/vc-select/Select.jsx
@@ -1291,7 +1291,6 @@ const Select = {
maxTagPlaceholderEl = ({clearIcon || ×}
)
diff --git a/types/select/select.d.ts b/types/select/select.d.ts
index b10effae7..81eaa917a 100644
--- a/types/select/select.d.ts
+++ b/types/select/select.d.ts
@@ -5,6 +5,7 @@
import { AntdComponent } from "../component";
import { Option } from "./option";
import { OptionGroup } from "./option-group";
+import { VNode } from "vue";
export declare class Select extends AntdComponent {
static Option: Option;
@@ -64,6 +65,12 @@ export declare class Select extends AntdComponent {
*/
dropdownMatchSelectWidth: boolean;
+ /**
+ * Customize dropdown content
+ * @type function | slot-scope
+ */
+ dropdownRender?: (menu?: VNode, props?: object) => VNode;
+
/**
* style of dropdown menu
* @type object
@@ -168,10 +175,28 @@ export declare class Select extends AntdComponent {
/**
* The custom suffix icon
- * @type any (string | VNode | slot)
+ * @type any (VNode | slot)
*/
suffixIcon: any;
+ /**
+ * The custom remove icon
+ * @type any (VNode | slot)
+ */
+ removeIcon: any;
+
+ /**
+ * The custom clear icon
+ * @type any (VNode | slot)
+ */
+ clearIcon: any;
+
+ /**
+ * The custom menuItemSelected icon
+ * @type any (VNode | slot)
+ */
+ menuItemSelectedIcon: any;
+
/**
* Separator used to tokenize on tag/multiple mode
* @type string[]