diff --git a/components/select/__tests__/__snapshots__/demo.test.js.snap b/components/select/__tests__/__snapshots__/demo.test.js.snap index 03b6f0fd4..66ca4b97d 100644 --- a/components/select/__tests__/__snapshots__/demo.test.js.snap +++ b/components/select/__tests__/__snapshots__/demo.test.js.snap @@ -2,7 +2,7 @@ exports[`renders ./components/select/demo/automatic-tokenization.md correctly 1`] = `
-
+
+
@@ -54,24 +86,24 @@ exports[`renders ./components/select/demo/coordinate.md correctly 1`] = ` exports[`renders ./components/select/demo/label-in-value.md correctly 1`] = `
-
+
Lucy (101)
-
+
`; exports[`renders ./components/select/demo/multiple.md correctly 1`] = `
-
+
- +
-
  • +
  • -
  • +
  • -

    -
    +
    - +
    - -
    +``` + 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 + + +``` + 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 = (