mirror of
https://gitee.com/ant-design-vue/ant-design-vue.git
synced 2024-12-03 04:27:41 +08:00
feat: update vc-select
This commit is contained in:
parent
e706170669
commit
435b8a19e6
@ -52,9 +52,11 @@ const getSlots = (ele) => {
|
||||
const children = ele.children || componentOptions.children || []
|
||||
const slots = {}
|
||||
children.forEach(child => {
|
||||
const name = (child.data && child.data.slot) || 'default'
|
||||
slots[name] = slots[name] || []
|
||||
slots[name].push(child)
|
||||
if (!isEmptyElement(child)) {
|
||||
const name = (child.data && child.data.slot) || 'default'
|
||||
slots[name] = slots[name] || []
|
||||
slots[name].push(child)
|
||||
}
|
||||
})
|
||||
return slots
|
||||
}
|
||||
@ -213,12 +215,12 @@ export function getComponentName (opts) {
|
||||
return opts && (opts.Ctor.options.name || opts.tag)
|
||||
}
|
||||
|
||||
export function isEmptyElement (ele) {
|
||||
return !(ele.tag || ele.text.trim() !== '')
|
||||
export function isEmptyElement (c) {
|
||||
return !(c.tag || (c.text && c.text.trim() !== ''))
|
||||
}
|
||||
|
||||
export function filterEmpty (children = []) {
|
||||
return children.filter(c => c.tag || (c.text && c.text.trim() !== ''))
|
||||
return children.filter(c => !isEmptyElement(c))
|
||||
}
|
||||
const initDefaultProps = (propTypes, defaultProps) => {
|
||||
Object.keys(defaultProps).forEach(k => {
|
||||
|
@ -11,6 +11,7 @@ export default {
|
||||
name: 'DropdownMenu',
|
||||
mixins: [BaseMixin],
|
||||
props: {
|
||||
ariaId: PropTypes.string,
|
||||
defaultActiveFirstOption: PropTypes.bool,
|
||||
value: PropTypes.any,
|
||||
dropdownMenuStyle: PropTypes.object,
|
||||
@ -28,8 +29,10 @@ export default {
|
||||
menuItemSelectedIcon: PropTypes.any,
|
||||
},
|
||||
|
||||
beforeMount () {
|
||||
created () {
|
||||
this.rafInstance = { cancel: () => null }
|
||||
this.lastInputValue = this.$props.inputValue
|
||||
this.lastVisible = false
|
||||
},
|
||||
|
||||
mounted () {
|
||||
@ -101,6 +104,7 @@ export default {
|
||||
firstActiveValue,
|
||||
dropdownMenuStyle,
|
||||
backfillValue,
|
||||
visible,
|
||||
} = props
|
||||
const menuItemSelectedIcon = getComponentFromProp(this, 'menuItemSelectedIcon')
|
||||
const { menuDeselect, menuSelect, popupScroll } = this.$listeners
|
||||
@ -136,6 +140,8 @@ export default {
|
||||
if (selectedKeys.length || firstActiveValue) {
|
||||
if (props.visible && !this.lastVisible) {
|
||||
activeKeyProps.activeKey = selectedKeys[0] !== undefined ? selectedKeys[0] : firstActiveValue
|
||||
} else if (!visible) {
|
||||
activeKeyProps.activeKey = undefined
|
||||
}
|
||||
let foundFirst = false
|
||||
// set firstActiveItem via cloning menus
|
||||
@ -143,9 +149,7 @@ export default {
|
||||
const clone = item => {
|
||||
if (
|
||||
(!foundFirst && selectedKeys.indexOf(item.key) !== -1) ||
|
||||
(!foundFirst &&
|
||||
!selectedKeys.length &&
|
||||
firstActiveValue.indexOf(item.key) !== -1)
|
||||
(!foundFirst && !selectedKeys.length && firstActiveValue.indexOf(item.key) !== -1)
|
||||
) {
|
||||
foundFirst = true
|
||||
return cloneElement(item, {
|
||||
@ -198,6 +202,8 @@ export default {
|
||||
overflow: 'auto',
|
||||
transform: 'translateZ(0)',
|
||||
}}
|
||||
id={this.$props.ariaId}
|
||||
tabIndex='-1'
|
||||
onFocus={popupFocus}
|
||||
onMousedown={preventDefaultEvent}
|
||||
onScroll={popupScroll}
|
||||
|
@ -2,7 +2,14 @@
|
||||
import PropTypes from '../_util/vue-types'
|
||||
export default {
|
||||
props: {
|
||||
label: PropTypes.any,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]),
|
||||
label: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]),
|
||||
},
|
||||
isSelectOptGroup: true,
|
||||
}
|
||||
|
@ -7,8 +7,15 @@ export default {
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]),
|
||||
label: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]),
|
||||
disabled: PropTypes.bool,
|
||||
title: PropTypes.string,
|
||||
title: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]),
|
||||
},
|
||||
isSelectOption: true,
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ export const SelectPropTypes = {
|
||||
placeholder: PropTypes.any,
|
||||
// onDeselect: PropTypes.func,
|
||||
labelInValue: PropTypes.bool,
|
||||
loading: PropTypes.bool,
|
||||
value: PropTypes.any,
|
||||
defaultValue: PropTypes.any,
|
||||
dropdownStyle: PropTypes.object,
|
||||
@ -47,4 +48,12 @@ export const SelectPropTypes = {
|
||||
inputIcon: PropTypes.any,
|
||||
removeIcon: PropTypes.any,
|
||||
menuItemSelectedIcon: PropTypes.any,
|
||||
dropdownRender: PropTypes.func,
|
||||
mode: PropTypes.oneOf(['multiple', 'tags']),
|
||||
backfill: PropTypes.bool,
|
||||
dropdownAlign: PropTypes.any,
|
||||
dropdownMatchSelectWidth: PropTypes.bool,
|
||||
dropdownMenuStyle: PropTypes.object,
|
||||
notFoundContent: PropTypes.oneOfType([String, Number]),
|
||||
tabIndex: PropTypes.oneOfType([String, Number]),
|
||||
}
|
||||
|
@ -7,56 +7,58 @@ import { Item as MenuItem, ItemGroup as MenuItemGroup } from '../vc-menu'
|
||||
import warning from 'warning'
|
||||
import Vue from 'vue'
|
||||
import Option from './Option'
|
||||
import { hasProp, getSlotOptions, getPropsData, getValueByProp as getValue, getComponentFromProp, getEvents, getClass, getStyle, getAttrs, getOptionProps } from '../_util/props-util'
|
||||
import OptGroup from './OptGroup'
|
||||
import { hasProp, getSlotOptions, getPropsData, getValueByProp as getValue, getComponentFromProp, getEvents, getClass, getStyle, getAttrs, getOptionProps, getSlots } from '../_util/props-util'
|
||||
import getTransitionProps from '../_util/getTransitionProps'
|
||||
import { cloneElement } from '../_util/vnode'
|
||||
import BaseMixin from '../_util/BaseMixin'
|
||||
import proxyComponent from '../_util/proxyComponent'
|
||||
import ref from 'vue-ref'
|
||||
|
||||
Vue.use(ref, { name: 'ant-ref' })
|
||||
|
||||
import SelectTrigger from './SelectTrigger'
|
||||
import {
|
||||
defaultFilterFn,
|
||||
findFirstMenuItem,
|
||||
findIndexInValueBySingleValue,
|
||||
generateUUID,
|
||||
getLabelFromPropsValue,
|
||||
getMapKey,
|
||||
getPropValue,
|
||||
getValuePropValue,
|
||||
includesSeparators,
|
||||
isCombobox,
|
||||
isMultipleOrTags,
|
||||
isMultipleOrTagsOrCombobox,
|
||||
isSingleMode,
|
||||
preventDefaultEvent,
|
||||
saveRef,
|
||||
splitBySeparators,
|
||||
toArray,
|
||||
getMapKey,
|
||||
findIndexInValueBySingleValue,
|
||||
getLabelFromPropsValue,
|
||||
toTitle,
|
||||
UNSELECTABLE_ATTRIBUTE,
|
||||
UNSELECTABLE_STYLE,
|
||||
preventDefaultEvent,
|
||||
findFirstMenuItem,
|
||||
includesSeparators,
|
||||
splitBySeparators,
|
||||
defaultFilterFn,
|
||||
validateOptionValue,
|
||||
saveRef,
|
||||
toTitle,
|
||||
} from './util'
|
||||
import SelectTrigger from './SelectTrigger'
|
||||
import { SelectPropTypes } from './PropTypes'
|
||||
|
||||
Vue.use(ref, { name: 'ant-ref' })
|
||||
const SELECT_EMPTY_VALUE_KEY = 'RC_SELECT_EMPTY_VALUE_KEY'
|
||||
|
||||
function noop () {}
|
||||
const noop = () => null
|
||||
|
||||
function chaining (...fns) {
|
||||
return function (...args) { // eslint-disable-line
|
||||
// eslint-disable-line
|
||||
for (let i = 0; i < fns.length; i++) {
|
||||
if (fns[i] && typeof fns[i] === 'function') {
|
||||
fns[i].apply(this, args)
|
||||
fns[i].apply(chaining, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const Select = {
|
||||
inheritAttrs: false,
|
||||
Option,
|
||||
OptGroup,
|
||||
name: 'Select',
|
||||
mixins: [BaseMixin],
|
||||
props: {
|
||||
@ -80,6 +82,8 @@ const Select = {
|
||||
combobox: PropTypes.bool.def(false),
|
||||
tokenSeparators: PropTypes.arrayOf(PropTypes.string).def([]),
|
||||
autoClearSearchValue: PropTypes.bool.def(true),
|
||||
tabIndex: PropTypes.any.def(0),
|
||||
dropdownRender: PropTypes.func.def(menu => menu),
|
||||
// onChange: noop,
|
||||
// onFocus: noop,
|
||||
// onBlur: noop,
|
||||
@ -99,6 +103,10 @@ const Select = {
|
||||
this.saveSelectTriggerRef = saveRef(this, 'selectTriggerRef')
|
||||
this.saveRootRef = saveRef(this, 'rootRef')
|
||||
this.saveSelectionRef = saveRef(this, 'selectionRef')
|
||||
this.ariaId = generateUUID()
|
||||
this._focused = false
|
||||
this._mouseDown = false
|
||||
this._options = []
|
||||
},
|
||||
data () {
|
||||
const props = getOptionProps(this)
|
||||
@ -116,6 +124,7 @@ const Select = {
|
||||
) : '',
|
||||
_open: props.defaultOpen,
|
||||
_optionsInfo: optionsInfo,
|
||||
_backfillValue: '',
|
||||
// a flag for aviod redundant getOptionsInfoFromProps call
|
||||
_skipBuildOptionsInfo: true,
|
||||
}
|
||||
@ -140,10 +149,10 @@ const Select = {
|
||||
if (isMultipleOrTags(this.$props)) {
|
||||
const inputNode = this.getInputDOMNode()
|
||||
const mirrorNode = this.getInputMirrorDOMNode()
|
||||
if (inputNode.value) {
|
||||
if (inputNode.value && inputNode.value && mirrorNode) {
|
||||
inputNode.style.width = ''
|
||||
inputNode.style.width = `${mirrorNode.clientWidth + 10}px`
|
||||
} else {
|
||||
} else if (inputNode) {
|
||||
inputNode.style.width = ''
|
||||
}
|
||||
}
|
||||
@ -185,6 +194,91 @@ const Select = {
|
||||
}
|
||||
return newState
|
||||
},
|
||||
getOptionsFromChildren (children = [], options = []) {
|
||||
children.forEach(child => {
|
||||
if (!child.data || child.data.slot !== undefined) {
|
||||
return
|
||||
}
|
||||
if (getSlotOptions(child).isSelectOptGroup) {
|
||||
this.getOptionsFromChildren(child.componentOptions.children, options)
|
||||
} else {
|
||||
options.push(child)
|
||||
}
|
||||
})
|
||||
return options
|
||||
},
|
||||
getInputValueForCombobox (props, optionsInfo, useDefaultValue) {
|
||||
let value = []
|
||||
if ('value' in props && !useDefaultValue) {
|
||||
value = toArray(props.value)
|
||||
}
|
||||
if ('defaultValue' in props && useDefaultValue) {
|
||||
value = toArray(props.defaultValue)
|
||||
}
|
||||
if (value.length) {
|
||||
value = value[0]
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
let label = value
|
||||
if (props.labelInValue) {
|
||||
label = value.label
|
||||
} else if (optionsInfo[getMapKey(value)]) {
|
||||
label = optionsInfo[getMapKey(value)].label
|
||||
}
|
||||
if (label === undefined) {
|
||||
label = ''
|
||||
}
|
||||
return label
|
||||
},
|
||||
|
||||
getLabelFromOption (props, option) {
|
||||
return getPropValue(option, props.optionLabelProp)
|
||||
},
|
||||
|
||||
getOptionsInfoFromProps (props, preState) {
|
||||
const options = this.getOptionsFromChildren(this.$props.children)
|
||||
const optionsInfo = {}
|
||||
options.forEach((option) => {
|
||||
const singleValue = getValuePropValue(option)
|
||||
optionsInfo[getMapKey(singleValue)] = {
|
||||
option,
|
||||
value: singleValue,
|
||||
label: this.getLabelFromOption(props, option),
|
||||
title: getValue(option, 'title'),
|
||||
}
|
||||
})
|
||||
if (preState) {
|
||||
// keep option info in pre state value.
|
||||
const oldOptionsInfo = preState._optionsInfo
|
||||
const value = preState._value
|
||||
if (value) {
|
||||
value.forEach(v => {
|
||||
const key = getMapKey(v)
|
||||
if (!optionsInfo[key] && oldOptionsInfo[key] !== undefined) {
|
||||
optionsInfo[key] = oldOptionsInfo[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
return optionsInfo
|
||||
},
|
||||
|
||||
getValueFromProps (props, useDefaultValue) {
|
||||
let value = []
|
||||
if ('value' in props && !useDefaultValue) {
|
||||
value = toArray(props.value)
|
||||
}
|
||||
if ('defaultValue' in props && useDefaultValue) {
|
||||
value = toArray(props.defaultValue)
|
||||
}
|
||||
if (props.labelInValue) {
|
||||
value = value.map((v) => {
|
||||
return v.key
|
||||
})
|
||||
}
|
||||
return value
|
||||
},
|
||||
|
||||
onInputChange (event) {
|
||||
const { tokenSeparators } = this.$props
|
||||
@ -290,7 +384,7 @@ const Select = {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.getRealOpenState(state)) {
|
||||
if (this.getRealOpenState(state) && this.selectTriggerRef) {
|
||||
const menu = this.selectTriggerRef.getInnerMenu()
|
||||
if (menu && menu.onKeyDown(event, this.handleBackfill)) {
|
||||
event.preventDefault()
|
||||
@ -324,12 +418,8 @@ const Select = {
|
||||
this.setOpenState(false, true)
|
||||
}
|
||||
this.fireChange(value)
|
||||
let inputValue
|
||||
if (isCombobox(props)) {
|
||||
inputValue = getPropValue(item, props.optionLabelProp)
|
||||
} else {
|
||||
inputValue = ''
|
||||
}
|
||||
const inputValue = isCombobox(props) ? getPropValue(item, props.optionLabelProp) : ''
|
||||
|
||||
if (props.autoClearSearchValue) {
|
||||
this.setInputValue(inputValue, false)
|
||||
}
|
||||
@ -357,7 +447,7 @@ const Select = {
|
||||
},
|
||||
|
||||
onPlaceholderClick (e) {
|
||||
if (this.getInputDOMNode()) {
|
||||
if (this.getInputDOMNode() && this.getInputDOMNode()) {
|
||||
this.getInputDOMNode().focus()
|
||||
}
|
||||
},
|
||||
@ -389,89 +479,6 @@ const Select = {
|
||||
onChoiceAnimationLeave () {
|
||||
this.forcePopupAlign()
|
||||
},
|
||||
getOptionsFromChildren (children = [], options = []) {
|
||||
children.forEach(child => {
|
||||
if (!child.data || child.data.slot !== undefined) {
|
||||
return
|
||||
}
|
||||
if (getSlotOptions(child).isSelectOptGroup) {
|
||||
this.getOptionsFromChildren(child.componentOptions.children, options)
|
||||
} else {
|
||||
options.push(child)
|
||||
}
|
||||
})
|
||||
return options
|
||||
},
|
||||
getInputValueForCombobox (props, optionsInfo, useDefaultValue) {
|
||||
let value = []
|
||||
if ('value' in props && !useDefaultValue) {
|
||||
value = toArray(props.value)
|
||||
}
|
||||
if ('defaultValue' in props && useDefaultValue) {
|
||||
value = toArray(props.defaultValue)
|
||||
}
|
||||
if (value.length) {
|
||||
value = value[0]
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
let label = value
|
||||
if (props.labelInValue) {
|
||||
label = value.label
|
||||
} else if (optionsInfo[getMapKey(value)]) {
|
||||
label = optionsInfo[getMapKey(value)].label
|
||||
}
|
||||
if (label === undefined) {
|
||||
label = ''
|
||||
}
|
||||
return label
|
||||
},
|
||||
|
||||
getLabelFromOption (props, option) {
|
||||
return getPropValue(option, props.optionLabelProp)
|
||||
},
|
||||
|
||||
getOptionsInfoFromProps (props, preState) {
|
||||
const options = this.getOptionsFromChildren(this.$props.children)
|
||||
const optionsInfo = {}
|
||||
options.forEach((option) => {
|
||||
const singleValue = getValuePropValue(option)
|
||||
optionsInfo[getMapKey(singleValue)] = {
|
||||
option,
|
||||
value: singleValue,
|
||||
label: this.getLabelFromOption(props, option),
|
||||
title: getValue(option, 'title'),
|
||||
}
|
||||
})
|
||||
if (preState) {
|
||||
// keep option info in pre state value.
|
||||
const oldOptionsInfo = preState._optionsInfo
|
||||
const value = preState._value
|
||||
value.forEach(v => {
|
||||
const key = getMapKey(v)
|
||||
if (!optionsInfo[key] && oldOptionsInfo[key] !== undefined) {
|
||||
optionsInfo[key] = oldOptionsInfo[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
return optionsInfo
|
||||
},
|
||||
|
||||
getValueFromProps (props, useDefaultValue) {
|
||||
let value = []
|
||||
if ('value' in props && !useDefaultValue) {
|
||||
value = toArray(props.value)
|
||||
}
|
||||
if ('defaultValue' in props && useDefaultValue) {
|
||||
value = toArray(props.defaultValue)
|
||||
}
|
||||
if (props.labelInValue) {
|
||||
value = value.map((v) => {
|
||||
return v.key
|
||||
})
|
||||
}
|
||||
return value
|
||||
},
|
||||
|
||||
getOptionInfoBySingleValue (value, optionsInfo) {
|
||||
let info
|
||||
@ -515,7 +522,8 @@ const Select = {
|
||||
let value = null
|
||||
Object.keys(this.$data._optionsInfo).forEach(key => {
|
||||
const info = this.$data._optionsInfo[key]
|
||||
if (toArray(info.label).join('') === label) {
|
||||
const oldLable = toArray(info.label)
|
||||
if (oldLable && oldLable.join('') === label) {
|
||||
value = info.value
|
||||
}
|
||||
})
|
||||
@ -532,8 +540,8 @@ const Select = {
|
||||
return value
|
||||
},
|
||||
|
||||
getVLForOnChange (vls_) {
|
||||
let vls = vls_
|
||||
getVLForOnChange (vlsS) {
|
||||
let vls = vlsS
|
||||
if (vls !== undefined) {
|
||||
if (!this.labelInValue) {
|
||||
vls = vls.map(v => v)
|
||||
@ -567,10 +575,11 @@ const Select = {
|
||||
if (state._inputValue) {
|
||||
hidden = true
|
||||
}
|
||||
if (state._value.length) {
|
||||
const value = state._value
|
||||
if (value.length) {
|
||||
hidden = true
|
||||
}
|
||||
if (isCombobox(props) && state._value.length === 1 && !state._value[0]) {
|
||||
if (isCombobox(props) && value.length === 1 && (state._value && !state._value[0])) {
|
||||
hidden = false
|
||||
}
|
||||
const placeholder = props.placeholder
|
||||
@ -634,10 +643,16 @@ const Select = {
|
||||
this.setInputValue('')
|
||||
} else {
|
||||
// why not use setState?
|
||||
this.$data._inputValue = this.getInputDOMNode().value = ''
|
||||
this.$data._inputValue = ''
|
||||
this.$nextTick(() => {
|
||||
if (this.getInputDOMNode && this.getInputDOMNode()) {
|
||||
this.getInputDOMNode().value = ''
|
||||
}
|
||||
})
|
||||
}
|
||||
value = this.getValueByInput(inputValue)
|
||||
if (value !== undefined) {
|
||||
const tmpValue = this.getValueByInput(inputValue)
|
||||
if (tmpValue !== undefined) {
|
||||
value = tmpValue
|
||||
this.fireChange(value)
|
||||
}
|
||||
}
|
||||
@ -677,9 +692,11 @@ const Select = {
|
||||
const props = this.$props
|
||||
const { _inputValue: inputValue } = this.$data
|
||||
const attrs = getAttrs(this)
|
||||
const defaultInput = <input id={attrs.id} autoComplete='off' />
|
||||
|
||||
const inputElement = props.getInputElement
|
||||
? props.getInputElement()
|
||||
: <input id={attrs.id} autoComplete='off'/>
|
||||
: defaultInput
|
||||
const inputCls = classnames(getClass(inputElement), {
|
||||
[`${props.prefixCls}-search__field`]: true,
|
||||
})
|
||||
@ -749,34 +766,38 @@ const Select = {
|
||||
},
|
||||
|
||||
getPopupDOMNode () {
|
||||
return this.selectTriggerRef.getPopupDOMNode()
|
||||
if (this.selectTriggerRef) {
|
||||
return this.selectTriggerRef.getPopupDOMNode()
|
||||
}
|
||||
},
|
||||
|
||||
getPopupMenuComponent () {
|
||||
return this.selectTriggerRef.getInnerMenu()
|
||||
if (this.selectTriggerRef) {
|
||||
return this.selectTriggerRef.getInnerMenu()
|
||||
}
|
||||
},
|
||||
|
||||
setOpenState (open, needFocus) {
|
||||
const { $props: props, $data: state } = this
|
||||
if (state._open === open) {
|
||||
this.maybeFocus(open, needFocus)
|
||||
this.maybeFocus(open, !!needFocus)
|
||||
return
|
||||
}
|
||||
this.__emit('dropdownVisibleChange', open)
|
||||
const nextState = {
|
||||
_open: open,
|
||||
_backfillValue: undefined,
|
||||
_backfillValue: '',
|
||||
}
|
||||
// clear search input value when open is false in singleMode.
|
||||
if (!open && isSingleMode(props) && props.showSearch) {
|
||||
this.setInputValue('', false)
|
||||
}
|
||||
if (!open) {
|
||||
this.maybeFocus(open, needFocus)
|
||||
this.maybeFocus(open, !!needFocus)
|
||||
}
|
||||
this.setState(nextState, () => {
|
||||
if (open) {
|
||||
this.maybeFocus(open, needFocus)
|
||||
this.maybeFocus(open, !!needFocus)
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -791,11 +812,11 @@ const Select = {
|
||||
}
|
||||
}
|
||||
},
|
||||
getValueByInput (string) {
|
||||
getValueByInput (str) {
|
||||
const { multiple, tokenSeparators } = this.$props
|
||||
let nextValue = this.$data._value
|
||||
let hasNewValue = false
|
||||
splitBySeparators(string, tokenSeparators).forEach(label => {
|
||||
splitBySeparators(str, tokenSeparators).forEach(label => {
|
||||
const selectedValue = [label]
|
||||
if (multiple) {
|
||||
const value = this.getValueByLabel(label)
|
||||
@ -804,13 +825,10 @@ const Select = {
|
||||
hasNewValue = true
|
||||
this.fireSelect(value)
|
||||
}
|
||||
} else {
|
||||
// tag
|
||||
if (findIndexInValueBySingleValue(nextValue, label) === -1) {
|
||||
nextValue = nextValue.concat(selectedValue)
|
||||
hasNewValue = true
|
||||
this.fireSelect(label)
|
||||
}
|
||||
} else if (findIndexInValueBySingleValue(nextValue, label) === -1) {
|
||||
nextValue = nextValue.concat(selectedValue)
|
||||
hasNewValue = true
|
||||
this.fireSelect(label)
|
||||
}
|
||||
})
|
||||
return hasNewValue ? nextValue : undefined
|
||||
@ -833,17 +851,17 @@ const Select = {
|
||||
},
|
||||
|
||||
focus () {
|
||||
if (isSingleMode(this.$props)) {
|
||||
if (isSingleMode(this.$props) && this.selectionRef) {
|
||||
this.selectionRef.focus()
|
||||
} else {
|
||||
} else if (this.getInputDOMNode()) {
|
||||
this.getInputDOMNode().focus()
|
||||
}
|
||||
},
|
||||
|
||||
blur () {
|
||||
if (isSingleMode(this.$props)) {
|
||||
if (isSingleMode(this.$props) && this.selectionRef) {
|
||||
this.selectionRef.blur()
|
||||
} else {
|
||||
} else if (this.getInputDOMNode()) {
|
||||
this.getInputDOMNode().blur()
|
||||
}
|
||||
},
|
||||
@ -880,11 +898,11 @@ const Select = {
|
||||
}
|
||||
let filterFn = this.$props.filterOption
|
||||
if (hasProp(this, 'filterOption')) {
|
||||
if (this.filterOption === true) {
|
||||
filterFn = defaultFilter
|
||||
if (filterFn === true) {
|
||||
filterFn = defaultFilter.bind(this)
|
||||
}
|
||||
} else {
|
||||
filterFn = defaultFilter
|
||||
filterFn = defaultFilter.bind(this)
|
||||
}
|
||||
if (!filterFn) {
|
||||
return true
|
||||
@ -900,7 +918,7 @@ const Select = {
|
||||
if (this.focusTimer) {
|
||||
this.clearFocusTime()
|
||||
}
|
||||
this.focusTimer = setTimeout(() => {
|
||||
this.focusTimer = window.setTimeout(() => {
|
||||
// this._focused = true
|
||||
// this.updateFocusClassName()
|
||||
this.$emit('focus')
|
||||
@ -940,7 +958,7 @@ const Select = {
|
||||
input.focus()
|
||||
this._focused = true
|
||||
}
|
||||
} else if (activeElement !== this.selectionRef) {
|
||||
} else if (activeElement !== this.selectionRef && this.selectionRef) {
|
||||
this.selectionRef.focus()
|
||||
this._focused = true
|
||||
}
|
||||
@ -956,8 +974,8 @@ const Select = {
|
||||
if (e && e.stopPropagation) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
const value = this.$data._value.filter(singleValue => {
|
||||
const oldValue = this.$data._value
|
||||
const value = oldValue.filter(singleValue => {
|
||||
return singleValue !== selectedKey
|
||||
})
|
||||
const canMultiple = isMultipleOrTags(props)
|
||||
@ -1006,7 +1024,9 @@ const Select = {
|
||||
if (!this.$data._open) {
|
||||
return
|
||||
}
|
||||
this.selectTriggerRef && this.selectTriggerRef.triggerRef.forcePopupAlign()
|
||||
if (this.selectTriggerRef && this.selectTriggerRef.triggerRef) {
|
||||
this.selectTriggerRef.triggerRef.forcePopupAlign()
|
||||
}
|
||||
},
|
||||
renderFilterOptions () {
|
||||
const { _inputValue: inputValue } = this.$data
|
||||
@ -1024,8 +1044,7 @@ const Select = {
|
||||
value = value.filter(singleValue => {
|
||||
return (
|
||||
childrenKeys.indexOf(singleValue) === -1 &&
|
||||
(!inputValue ||
|
||||
String(singleValue).indexOf(String(inputValue)) > -1)
|
||||
(!inputValue || String(singleValue).indexOf(String(inputValue)) > -1)
|
||||
)
|
||||
})
|
||||
value.forEach(singleValue => {
|
||||
@ -1109,25 +1128,49 @@ const Select = {
|
||||
return
|
||||
}
|
||||
if (getSlotOptions(child).isSelectOptGroup) {
|
||||
const innerItems = this.renderFilterOptionsFromChildren(
|
||||
child.componentOptions.children,
|
||||
childrenKeys,
|
||||
menuItems,
|
||||
)
|
||||
if (innerItems.length) {
|
||||
let label = getComponentFromProp(child, 'label')
|
||||
let key = child.key
|
||||
if (!key && typeof label === 'string') {
|
||||
key = label
|
||||
} else if (!label && key) {
|
||||
label = key
|
||||
}
|
||||
let label = getComponentFromProp(child, 'label')
|
||||
let key = child.key
|
||||
if (!key && typeof label === 'string') {
|
||||
key = label
|
||||
} else if (!label && key) {
|
||||
label = key
|
||||
}
|
||||
const childChildren = getSlots(child).default
|
||||
// Match option group label
|
||||
if (inputValue && this._filterOption(inputValue, child)) {
|
||||
const innerItems = childChildren.map(
|
||||
(subChild) => {
|
||||
const childValueSub = getValuePropValue(subChild) || subChild.key
|
||||
return (
|
||||
<MenuItem key={childValueSub} value={childValueSub} {...subChild.data}>
|
||||
{subChild.componentOptions.children}
|
||||
</MenuItem>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
sel.push(
|
||||
<MenuItemGroup key={key} title={label} class ={getClass(child)}>
|
||||
{innerItems}
|
||||
</MenuItemGroup>
|
||||
</MenuItemGroup>,
|
||||
)
|
||||
|
||||
// Not match
|
||||
} else {
|
||||
const innerItems = this.renderFilterOptionsFromChildren(
|
||||
childChildren,
|
||||
childrenKeys,
|
||||
menuItems,
|
||||
)
|
||||
if (innerItems.length) {
|
||||
sel.push(
|
||||
<MenuItemGroup key={key} title={label} {...child.data}>
|
||||
{innerItems}
|
||||
</MenuItemGroup>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
warning(
|
||||
@ -1242,11 +1285,14 @@ const Select = {
|
||||
let content = `+ ${value.length - maxTagCount} ...`
|
||||
if (maxTagPlaceholder) {
|
||||
content = typeof maxTagPlaceholder === 'function'
|
||||
? maxTagPlaceholder(omittedValues) : maxTagPlaceholder
|
||||
? maxTagPlaceholder(omittedValues)
|
||||
: maxTagPlaceholder
|
||||
}
|
||||
maxTagPlaceholderEl = (<li
|
||||
style={UNSELECTABLE_STYLE}
|
||||
{...{ attrs: UNSELECTABLE_ATTRIBUTE }}
|
||||
unselectable='unselectable'
|
||||
role='presentation'
|
||||
onMousedown={preventDefaultEvent}
|
||||
class={`${prefixCls}-selection__choice ${prefixCls}-selection__choice__disabled`}
|
||||
key='maxTagPlaceholder'
|
||||
@ -1274,9 +1320,11 @@ const Select = {
|
||||
return (
|
||||
<li
|
||||
style={UNSELECTABLE_STYLE}
|
||||
{...{ attrs: UNSELECTABLE_ATTRIBUTE }}
|
||||
unselectable='unselectable'
|
||||
onMousedown={preventDefaultEvent}
|
||||
class={choiceClassName}
|
||||
role='presentation'
|
||||
key={singleValue || SELECT_EMPTY_VALUE_KEY}
|
||||
title={toTitle(title)}
|
||||
>
|
||||
@ -1321,11 +1369,7 @@ const Select = {
|
||||
</transition-group>
|
||||
)
|
||||
} else {
|
||||
innerNode = (
|
||||
<ul>
|
||||
{selectedValueNodes}
|
||||
</ul>
|
||||
)
|
||||
innerNode = <ul>{selectedValueNodes}</ul>
|
||||
}
|
||||
}
|
||||
return (
|
||||
@ -1342,6 +1386,33 @@ const Select = {
|
||||
</div>
|
||||
)
|
||||
},
|
||||
renderArrow (multiple) {
|
||||
const { showArrow, loading, prefixCls } = this.$props
|
||||
const inputIcon = getComponentFromProp(this, 'inputIcon')
|
||||
if (!showArrow) {
|
||||
return null
|
||||
}
|
||||
// if loading have loading icon
|
||||
if (multiple && !loading) {
|
||||
return null
|
||||
}
|
||||
const defaultIcon = loading ? (
|
||||
<i class={`${prefixCls}-arrow-loading`} />
|
||||
) : (
|
||||
<i class={`${prefixCls}-arrow-icon`} />
|
||||
)
|
||||
return (
|
||||
<span
|
||||
key='arrow'
|
||||
class={`${prefixCls}-arrow`}
|
||||
style={UNSELECTABLE_STYLE}
|
||||
{...{ attrs: UNSELECTABLE_ATTRIBUTE }}
|
||||
onClick={this.onArrowClick}
|
||||
>
|
||||
{inputIcon || defaultIcon}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
topCtrlContainerClick (e) {
|
||||
if (this.$data._open && !isSingleMode(this.$props)) {
|
||||
e.stopPropagation()
|
||||
@ -1412,7 +1483,6 @@ const Select = {
|
||||
const multiple = isMultipleOrTags(props)
|
||||
const state = this.$data
|
||||
const { disabled, prefixCls } = props
|
||||
const inputIcon = getComponentFromProp(this, 'inputIcon')
|
||||
const ctrlNode = this.renderTopControlNode()
|
||||
const { _open: open, _inputValue: inputValue, _value: value } = this.$data
|
||||
if (open) {
|
||||
@ -1429,6 +1499,7 @@ const Select = {
|
||||
'aria-autocomplete': 'list',
|
||||
'aria-haspopup': 'true',
|
||||
'aria-expanded': realOpen,
|
||||
'aria-controls': this.ariaId,
|
||||
},
|
||||
on: {
|
||||
click: this.selectionRefClick,
|
||||
@ -1445,7 +1516,7 @@ const Select = {
|
||||
selectionProps.on.keydown = this.onKeyDown
|
||||
selectionProps.on.focus = this.selectionRefFocus
|
||||
selectionProps.on.blur = this.selectionRefBlur
|
||||
selectionProps.attrs.tabIndex = props.disabled ? -1 : 0
|
||||
selectionProps.attrs.tabIndex = props.disabled ? -1 : props.tabIndex
|
||||
}
|
||||
const rootCls = {
|
||||
[prefixCls]: true,
|
||||
@ -1492,6 +1563,8 @@ const Select = {
|
||||
name: 'ant-ref',
|
||||
value: this.saveSelectTriggerRef,
|
||||
}] }}
|
||||
dropdownRender={props.dropdownRender}
|
||||
ariaId={this.ariaId}
|
||||
>
|
||||
<div
|
||||
{...{ directives: [{
|
||||
@ -1510,16 +1583,7 @@ const Select = {
|
||||
<div {...selectionProps}>
|
||||
{ctrlNode}
|
||||
{this.renderClear()}
|
||||
{multiple || !props.showArrow ? null : (
|
||||
<span
|
||||
key='arrow'
|
||||
class={`${prefixCls}-arrow`}
|
||||
style={UNSELECTABLE_STYLE}
|
||||
unselectable='unselectable'
|
||||
// onClick={this.onArrowClick}
|
||||
>
|
||||
{inputIcon || <i class={`${prefixCls}-arrow-icon`} />}
|
||||
</span>)}
|
||||
{this.renderArrow(!!multiple)}
|
||||
</div>
|
||||
</div>
|
||||
</SelectTrigger>
|
||||
|
@ -55,6 +55,8 @@ export default {
|
||||
getPopupContainer: PropTypes.func,
|
||||
backfillValue: PropTypes.any,
|
||||
menuItemSelectedIcon: PropTypes.any,
|
||||
dropdownRender: PropTypes.func,
|
||||
ariaId: PropTypes.string,
|
||||
},
|
||||
created () {
|
||||
this.saveDropdownMenuRef = saveRef(this, 'dropdownMenuRef')
|
||||
@ -62,7 +64,7 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
dropdownWidth: null,
|
||||
dropdownWidth: 0,
|
||||
}
|
||||
},
|
||||
|
||||
@ -99,9 +101,13 @@ export default {
|
||||
dropdownMenuStyle, getDropdownPrefixCls, backfillValue, menuItemSelectedIcon,
|
||||
} = this
|
||||
const { menuSelect, menuDeselect, popupScroll } = this.$listeners
|
||||
const props = this.$props
|
||||
|
||||
const { dropdownRender, ariaId } = props
|
||||
const dropdownMenuProps = {
|
||||
props: {
|
||||
...newProps.props,
|
||||
ariaId,
|
||||
prefixCls: getDropdownPrefixCls(),
|
||||
value, firstActiveValue, defaultActiveFirstOption, dropdownMenuStyle,
|
||||
backfillValue,
|
||||
@ -118,9 +124,12 @@ export default {
|
||||
value: this.saveDropdownMenuRef,
|
||||
}],
|
||||
}
|
||||
return (
|
||||
<DropdownMenu {...dropdownMenuProps} />
|
||||
)
|
||||
const menuNode = <DropdownMenu {...dropdownMenuProps} />
|
||||
|
||||
if (dropdownRender) {
|
||||
return dropdownRender(menuNode, props)
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
getDropdownTransitionName () {
|
||||
|
@ -1,7 +1,16 @@
|
||||
@selectPrefixCls: rc-select;
|
||||
|
||||
@keyframes select-ring {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.effect() {
|
||||
animation-duration: .3s;
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
@ -14,7 +23,8 @@
|
||||
color: #666;
|
||||
line-height: 28px;
|
||||
|
||||
ul, li {
|
||||
ul,
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
@ -35,17 +45,36 @@
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&-arrow &-arrow-loading {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-top: 6px;
|
||||
margin-left: -4px;
|
||||
&:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin: 2px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #999999;
|
||||
border-color: #999999 transparent #999999 transparent;
|
||||
animation: select-ring 1.2s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
&-arrow &-arrow-icon {
|
||||
border-color: #999999 transparent transparent transparent;
|
||||
border-style: solid;
|
||||
border-width: 5px 4px 0 4px;
|
||||
height: 0;
|
||||
width: 0;
|
||||
margin-left: -4px;
|
||||
margin-top: -2px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
border-style: solid;
|
||||
border-width: 5px 4px 0 4px;
|
||||
height: 0;
|
||||
width: 0;
|
||||
margin-left: -4px;
|
||||
margin-top: -2px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
&-selection {
|
||||
@ -251,15 +280,18 @@
|
||||
margin-right: 4px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: padding .3s cubic-bezier(0.6, -0.28, 0.735, 0.045), width .3s cubic-bezier(0.6, -0.28, 0.735, 0.045);
|
||||
transition: padding 0.3s cubic-bezier(0.6, -0.28, 0.735, 0.045),
|
||||
width 0.3s cubic-bezier(0.6, -0.28, 0.735, 0.045);
|
||||
|
||||
&__content {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
transition: margin .3s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
transition: margin 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
}
|
||||
|
||||
&-zoom-enter, &-zoom-appear, &-zoom-leave {
|
||||
&-zoom-enter,
|
||||
&-zoom-appear,
|
||||
&-zoom-leave {
|
||||
.effect();
|
||||
opacity: 0;
|
||||
animation-play-state: paused;
|
||||
@ -310,7 +342,7 @@
|
||||
transform: scale(0);
|
||||
top: 0;
|
||||
right: 2px;
|
||||
transition: opacity .3s, transform .3s;
|
||||
transition: opacity 0.3s, transform 0.3s;
|
||||
|
||||
&-icon {
|
||||
font-style: normal;
|
||||
@ -402,7 +434,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-slide-up-enter, &-slide-up-appear {
|
||||
&-slide-up-enter,
|
||||
&-slide-up-appear {
|
||||
.effect();
|
||||
opacity: 0;
|
||||
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
|
||||
@ -416,7 +449,8 @@
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
&-slide-up-enter&-slide-up-enter-active&-placement-bottomLeft, &-slide-up-appear&-slide-up-appear-active&-placement-bottomLeft {
|
||||
&-slide-up-enter&-slide-up-enter-active&-placement-bottomLeft,
|
||||
&-slide-up-appear&-slide-up-appear-active&-placement-bottomLeft {
|
||||
animation-name: rcSelectDropdownSlideUpIn;
|
||||
animation-play-state: running;
|
||||
}
|
||||
@ -426,7 +460,8 @@
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
&-slide-up-enter&-slide-up-enter-active&-placement-topLeft, &-slide-up-appear&-slide-up-appear-active&-placement-topLeft {
|
||||
&-slide-up-enter&-slide-up-enter-active&-placement-topLeft,
|
||||
&-slide-up-appear&-slide-up-appear-active&-placement-topLeft {
|
||||
animation-name: rcSelectDropdownSlideDownIn;
|
||||
animation-play-state: running;
|
||||
}
|
||||
@ -493,5 +528,4 @@
|
||||
border-width: 0 4px 5px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ export function toTitle (title) {
|
||||
if (typeof title === 'string') {
|
||||
return title
|
||||
}
|
||||
return null
|
||||
return ''
|
||||
}
|
||||
export function getValuePropValue (child) {
|
||||
if (!child) {
|
||||
@ -24,9 +24,7 @@ export function getValuePropValue (child) {
|
||||
return label
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`Need at least a key or a value or a label(slot) (only for OptGroup) for ${child}`
|
||||
)
|
||||
throw new Error(`Need at least a key or a value or a label (only for OptGroup) for ${child}`)
|
||||
}
|
||||
|
||||
export function getPropValue (child, prop) {
|
||||
@ -88,10 +86,12 @@ export function preventDefaultEvent (e) {
|
||||
|
||||
export function findIndexInValueBySingleValue (value, singleValue) {
|
||||
let index = -1
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (value[i] === singleValue) {
|
||||
index = i
|
||||
break
|
||||
if (value) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (value[i] === singleValue) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return index
|
||||
@ -100,10 +100,12 @@ export function findIndexInValueBySingleValue (value, singleValue) {
|
||||
export function getLabelFromPropsValue (value, key) {
|
||||
let label
|
||||
value = toArray(value)
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (value[i].key === key) {
|
||||
label = value[i].label
|
||||
break
|
||||
if (value) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (value[i].key === key) {
|
||||
label = value[i].label
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return label
|
||||
@ -155,18 +157,18 @@ export function findFirstMenuItem (children) {
|
||||
return null
|
||||
}
|
||||
|
||||
export function includesSeparators (string, separators) {
|
||||
export function includesSeparators (str, separators) {
|
||||
for (let i = 0; i < separators.length; ++i) {
|
||||
if (string.lastIndexOf(separators[i]) > 0) {
|
||||
if (str.lastIndexOf(separators[i]) > 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function splitBySeparators (string, separators) {
|
||||
export function splitBySeparators (str, separators) {
|
||||
const reg = new RegExp(`[${separators.join()}]`)
|
||||
return string.split(reg).filter(token => token)
|
||||
return str.split(reg).filter(token => token)
|
||||
}
|
||||
|
||||
export function defaultFilterFn (input, child) {
|
||||
@ -180,9 +182,7 @@ export function defaultFilterFn (input, child) {
|
||||
} else {
|
||||
value = String(value)
|
||||
}
|
||||
return (
|
||||
value.toLowerCase().indexOf(input.toLowerCase()) > -1
|
||||
)
|
||||
return value.toLowerCase().indexOf(input.toLowerCase()) > -1
|
||||
}
|
||||
|
||||
export function validateOptionValue (value, props) {
|
||||
@ -192,7 +192,7 @@ export function validateOptionValue (value, props) {
|
||||
if (typeof value !== 'string') {
|
||||
throw new Error(
|
||||
`Invalid \`value\` of type \`${typeof value}\` supplied to Option, ` +
|
||||
`expected \`string\` when \`tags/combobox\` is \`true\`.`
|
||||
`expected \`string\` when \`tags/combobox\` is \`true\`.`,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -202,3 +202,16 @@ export function saveRef (instance, name) {
|
||||
instance[name] = node
|
||||
}
|
||||
}
|
||||
|
||||
export function generateUUID () {
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
return 'test-uuid'
|
||||
}
|
||||
let d = new Date().getTime()
|
||||
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
||||
const r = (d + Math.random() * 16) % 16 | 0
|
||||
d = Math.floor(d / 16)
|
||||
return (c === 'x' ? r : (r & 0x7) | 0x8).toString(16)
|
||||
})
|
||||
return uuid
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user