You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
3.0 KiB
135 lines
3.0 KiB
|
|
import PropTypes from '../_util/vue-types'
|
|
import BaseMixin from '../_util/BaseMixin'
|
|
import classnames from 'classnames'
|
|
function noop () {}
|
|
const scrollTo = (element, to, duration) => {
|
|
const requestAnimationFrame = window.requestAnimationFrame ||
|
|
function requestAnimationFrameTimeout () {
|
|
return setTimeout(arguments[0], 10)
|
|
}
|
|
// jump to target if duration zero
|
|
if (duration <= 0) {
|
|
element.scrollTop = to
|
|
return
|
|
}
|
|
const difference = to - element.scrollTop
|
|
const perTick = difference / duration * 10
|
|
|
|
requestAnimationFrame(() => {
|
|
element.scrollTop = element.scrollTop + perTick
|
|
if (element.scrollTop === to) return
|
|
scrollTo(element, to, duration - 10)
|
|
})
|
|
}
|
|
|
|
const Select = {
|
|
mixins: [BaseMixin],
|
|
props: {
|
|
prefixCls: PropTypes.string,
|
|
options: PropTypes.array,
|
|
selectedIndex: PropTypes.number,
|
|
type: PropTypes.string,
|
|
// onSelect: PropTypes.func,
|
|
// onMouseEnter: PropTypes.func,
|
|
},
|
|
data () {
|
|
return {
|
|
active: false,
|
|
}
|
|
},
|
|
|
|
mounted () {
|
|
this.$nextTick(() => {
|
|
// jump to selected option
|
|
this.scrollToSelected(0)
|
|
})
|
|
},
|
|
watch: {
|
|
selectedIndex (val) {
|
|
this.$nextTick(() => {
|
|
// smooth scroll to selected option
|
|
this.scrollToSelected(120)
|
|
})
|
|
},
|
|
},
|
|
methods: {
|
|
onSelect (value) {
|
|
const { type } = this
|
|
this.__emit('select', type, value)
|
|
},
|
|
|
|
getOptions () {
|
|
const { options, selectedIndex, prefixCls } = this
|
|
return options.map((item, index) => {
|
|
const cls = classnames({
|
|
[`${prefixCls}-select-option-selected`]: selectedIndex === index,
|
|
[`${prefixCls}-select-option-disabled`]: item.disabled,
|
|
})
|
|
let onClick = noop
|
|
if (!item.disabled) {
|
|
onClick = this.onSelect.bind(this, item.value)
|
|
}
|
|
return (<li
|
|
class={cls}
|
|
key={index}
|
|
onClick={onClick}
|
|
disabled={item.disabled}
|
|
>
|
|
{item.value}
|
|
</li>)
|
|
})
|
|
},
|
|
|
|
scrollToSelected (duration) {
|
|
// move to selected item
|
|
const select = this.$el
|
|
const list = this.$refs.list
|
|
if (!list) {
|
|
return
|
|
}
|
|
let index = this.selectedIndex
|
|
if (index < 0) {
|
|
index = 0
|
|
}
|
|
const topOption = list.children[index]
|
|
const to = topOption.offsetTop
|
|
scrollTo(select, to, duration)
|
|
},
|
|
|
|
handleMouseEnter (e) {
|
|
this.setState({ active: true })
|
|
this.__emit('mouseenter', e)
|
|
},
|
|
|
|
handleMouseLeave () {
|
|
this.setState({ active: false })
|
|
},
|
|
},
|
|
|
|
render () {
|
|
if (this.options.length === 0) {
|
|
return null
|
|
}
|
|
|
|
const { prefixCls } = this
|
|
const cls = {
|
|
[`${prefixCls}-select`]: 1,
|
|
[`${prefixCls}-select-active`]: this.active,
|
|
}
|
|
|
|
return (
|
|
<div
|
|
class={cls}
|
|
onMouseenter={this.handleMouseEnter}
|
|
onMouseleave={this.handleMouseLeave}
|
|
>
|
|
<ul ref='list'>{this.getOptions()}</ul>
|
|
</div>
|
|
)
|
|
},
|
|
}
|
|
|
|
export default Select
|
|
|