237 lines
5.6 KiB
Vue
237 lines
5.6 KiB
Vue
|
<template>
|
||
|
<div class="md-drop-menu">
|
||
|
<div class="md-drop-menu-bar">
|
||
|
<template v-for="(item, index) in data">
|
||
|
<div
|
||
|
class="bar-item"
|
||
|
:class="{
|
||
|
active: index === activeMenuBarIndex,
|
||
|
selected: $_checkBarItemSelect(index),
|
||
|
disabled: item.disabled
|
||
|
}"
|
||
|
:key="index"
|
||
|
@click="$_onBarItemClick(item, index)"
|
||
|
>
|
||
|
<span
|
||
|
v-text="$_getBarItemText(item, index)"
|
||
|
></span>
|
||
|
</div>
|
||
|
</template>
|
||
|
</div>
|
||
|
<md-popup
|
||
|
v-model="isPopupShow"
|
||
|
position="top"
|
||
|
@show="$_onListShow"
|
||
|
@hide="$_onListHide"
|
||
|
>
|
||
|
<div class="md-drop-menu-list">
|
||
|
<md-radio
|
||
|
v-model="selectedMenuListValue[activeMenuBarIndex]"
|
||
|
:options="activeMenuListData"
|
||
|
:optionRender="optionRender"
|
||
|
:is-slot-scope="hasSlot"
|
||
|
@change="$_onListItemClick"
|
||
|
>
|
||
|
<template slot-scope="{ option }">
|
||
|
<slot :option="option"></slot>
|
||
|
</template>
|
||
|
</md-radio>
|
||
|
</div>
|
||
|
</md-popup>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<script>
import Popup from '../popup'
|
||
|
import Radio from '../radio'
|
||
|
import {noop, traverse, compareObjects} from '../_util'
|
||
|
|
||
|
export default {
|
||
|
name: 'md-drop-menu',
|
||
|
|
||
|
components: {
|
||
|
[Popup.name]: Popup,
|
||
|
[Radio.name]: Radio,
|
||
|
},
|
||
|
|
||
|
props: {
|
||
|
data: {
|
||
|
type: Array,
|
||
|
default() {
|
||
|
return []
|
||
|
},
|
||
|
},
|
||
|
defaultValue: {
|
||
|
type: Array,
|
||
|
default() {
|
||
|
return []
|
||
|
},
|
||
|
},
|
||
|
optionRender: {
|
||
|
type: Function,
|
||
|
default: noop,
|
||
|
},
|
||
|
},
|
||
|
|
||
|
data() {
|
||
|
return {
|
||
|
isPopupShow: false,
|
||
|
selectedMenuListItem: [],
|
||
|
selectedMenuListValue: [],
|
||
|
selectedMenuListIndex: [],
|
||
|
activeMenuBarIndex: -1,
|
||
|
activeMenuListData: [],
|
||
|
}
|
||
|
},
|
||
|
|
||
|
computed: {
|
||
|
hasSlot() {
|
||
|
return !!this.$scopedSlots.default
|
||
|
},
|
||
|
},
|
||
|
|
||
|
watch: {
|
||
|
data(val, oldVal) {
|
||
|
// Avoid Literals
|
||
|
/* istanbul ignore if */
|
||
|
if (!compareObjects(val, oldVal)) {
|
||
|
this.$_initSelectedBar()
|
||
|
}
|
||
|
},
|
||
|
defaultValue(val, oldVal) {
|
||
|
/* istanbul ignore if */
|
||
|
if (!compareObjects(val, oldVal)) {
|
||
|
this.$_initSelectedBar()
|
||
|
}
|
||
|
},
|
||
|
},
|
||
|
|
||
|
mounted() {
|
||
|
this.$_initSelectedBar()
|
||
|
},
|
||
|
|
||
|
methods: {
|
||
|
// MARK: private methods
|
||
|
$_initSelectedBar() {
|
||
|
this.selectedMenuListValue = this.defaultValue
|
||
|
traverse(this.data, ['options'], (item, level, indexs) => {
|
||
|
const barItemIndex = indexs[0]
|
||
|
const defaultValue = this.defaultValue[barItemIndex]
|
||
|
if (
|
||
|
defaultValue !== undefined &&
|
||
|
(item.value === defaultValue || item.text === defaultValue || item.label === defaultValue)
|
||
|
) {
|
||
|
this.$set(this.selectedMenuListItem, barItemIndex, item)
|
||
|
return 2
|
||
|
}
|
||
|
})
|
||
|
},
|
||
|
$_checkBarItemSelect(index) {
|
||
|
return !!(this.selectedMenuListItem[index] !== undefined || this.defaultValue[index])
|
||
|
},
|
||
|
$_getBarItemText(item, index) {
|
||
|
return this.selectedMenuListItem[index] !== undefined ? this.selectedMenuListItem[index].text : item.text
|
||
|
},
|
||
|
|
||
|
// MARK: events handler
|
||
|
$_onBarItemClick(barItem, index) {
|
||
|
/* istanbul ignore if */
|
||
|
if (!barItem || barItem.disabled) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (!this.isPopupShow) {
|
||
|
this.isPopupShow = true
|
||
|
this.activeMenuBarIndex = index
|
||
|
this.activeMenuListData = barItem.options
|
||
|
} else {
|
||
|
this.isPopupShow = false
|
||
|
}
|
||
|
},
|
||
|
$_onListItemClick(listItem) {
|
||
|
const activeMenuBarIndex = this.activeMenuBarIndex
|
||
|
const barItem = this.data[activeMenuBarIndex]
|
||
|
this.isPopupShow = false
|
||
|
this.$set(this.selectedMenuListItem, activeMenuBarIndex, listItem)
|
||
|
this.$emit('change', barItem, listItem)
|
||
|
},
|
||
|
$_onListShow() {
|
||
|
/* istanbul ignore next */
|
||
|
this.$emit('show')
|
||
|
},
|
||
|
$_onListHide() {
|
||
|
/* istanbul ignore next */
|
||
|
this.activeMenuBarIndex = -1
|
||
|
/* istanbul ignore next */
|
||
|
this.$emit('hide')
|
||
|
},
|
||
|
|
||
|
// MARK: public methods
|
||
|
getSelectedValues() {
|
||
|
return this.selectedMenuListItem
|
||
|
},
|
||
|
getSelectedValue(index) {
|
||
|
return this.selectedMenuListItem[index]
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<style lang="stylus">
|
||
|
.md-drop-menu
|
||
|
position fixed
|
||
|
z-index drop-menu-zindex
|
||
|
top 0
|
||
|
left 0
|
||
|
right 0
|
||
|
height drop-menu-height
|
||
|
padding-bottom constant(safe-area-inset-bottom)
|
||
|
box-sizing border-box
|
||
|
color color-text-minor
|
||
|
font-size drop-menu-font-size
|
||
|
.md-drop-menu-bar
|
||
|
z-index drop-menu-zindex
|
||
|
display flex
|
||
|
height 100%
|
||
|
background color-bg-base
|
||
|
hairline(bottom, color-border-base)
|
||
|
.bar-item
|
||
|
display flex
|
||
|
flex 1
|
||
|
margin 2% 0
|
||
|
align-items center
|
||
|
justify-content center
|
||
|
border-right dotted 2px color-border-base
|
||
|
span
|
||
|
position relative
|
||
|
padding-right 30px
|
||
|
&:after
|
||
|
content ""
|
||
|
position absolute
|
||
|
right 0
|
||
|
top 50%
|
||
|
width 0
|
||
|
height 0
|
||
|
margin-top -4px
|
||
|
border-left solid 8px transparent
|
||
|
border-right solid 8px transparent
|
||
|
border-top solid 9px color-text-minor
|
||
|
border-radius 4px
|
||
|
transition transform .3s ease-in-out-quint
|
||
|
&.active
|
||
|
span:after
|
||
|
transform rotate(180deg)
|
||
|
&.selected
|
||
|
color drop-menu-color
|
||
|
span:after
|
||
|
border-top-color drop-menu-color
|
||
|
&.disabled
|
||
|
opacity opacity-disabled
|
||
|
&:last-of-type
|
||
|
border none
|
||
|
.md-drop-menu-list
|
||
|
width 100%
|
||
|
padding-top drop-menu-height
|
||
|
background color-bg-base
|
||
|
box-sizing border-box
|
||
|
</style>
|