mand-mobile/components/popup/index.vue

345 lines
8.2 KiB
Vue
Raw Normal View History

2018-03-26 16:04:04 +08:00
<template>
<div
v-show="isPopupShow"
class="md-popup"
:class="[
hasMask ? 'with-mask' : '',
position
]"
>
<transition name="fade">
<div
v-show="hasMask && isPopupBoxShow"
@click="$_onPopupMaskClick"
class="md-popup-mask"
></div>
</transition>
<transition
:name="transition"
@before-enter="$_onPopupTransitionStart"
@before-leave="$_onPopupTransitionStart"
@after-enter="$_onPopupTransitionEnd"
@after-leave="$_onPopupTransitionEnd"
>
<div
v-show="isPopupBoxShow"
class="md-popup-box"
:class="[
transition
]"
>
<slot></slot>
</div>
</transition>
</div>
</template>
<script> export default {
name: 'md-popup',
props: {
value: {
type: Boolean,
default: false,
},
hasMask: {
type: Boolean,
default: true,
},
maskClosable: {
type: Boolean,
default: true,
},
position: {
type: String,
default: 'center',
},
transition: {
type: String,
default() {
switch (this.position) {
case 'bottom':
return 'slide-up'
/* istanbul ignore next */
case 'top':
return 'slide-down'
/* istanbul ignore next */
case 'left':
return 'slide-right'
/* istanbul ignore next */
case 'right':
return 'slide-left'
default:
2018-09-03 16:51:17 +08:00
return 'fade' // fade/fade-bounce/fade-slide/fade-zoom
2018-03-26 16:04:04 +08:00
}
},
},
preventScroll: {
type: Boolean,
default: false,
},
preventScrollExclude: {
type: [String, HTMLElement],
default() {
return ''
},
},
},
data() {
return {
// controle popup mask & popup box
isPopupShow: false,
// controle popup box
isPopupBoxShow: false,
// transtion lock
isAnimation: false,
}
},
watch: {
value(val) {
/* istanbul ignore next */
if (val) {
if (this.isAnimation) {
setTimeout(() => {
this.$_showPopupBox()
}, 50)
} else {
this.$_showPopupBox()
}
} else {
this.$_hidePopupBox()
}
},
preventScrollExclude(val, oldVal) {
// remove old listener before add
2018-04-23 17:33:32 +08:00
/* istanbul ignore next */
this.$_preventScrollExclude(false, oldVal)
2018-04-23 17:33:32 +08:00
/* istanbul ignore next */
this.$_preventScrollExclude(true, val)
},
2018-03-26 16:04:04 +08:00
},
mounted() {
this.value && this.$_showPopupBox()
},
methods: {
// MARK: private methods
$_showPopupBox() {
this.isPopupShow = true
this.isAnimation = true
// popup box enter the animation after popup show
this.$nextTick(() => {
this.isPopupBoxShow = true
2018-04-23 17:33:32 +08:00
/* istanbul ignore if */
2018-03-26 16:04:04 +08:00
if (process.env.NODE_ENV === 'testing') {
this.$_onPopupTransitionEnd()
}
})
2018-03-26 16:04:04 +08:00
this.preventScroll && this.$_preventScroll(true)
},
$_hidePopupBox() {
this.isAnimation = true
this.isPopupBoxShow = false
this.preventScroll && this.$_preventScroll(false)
this.$emit('input', false)
2018-04-23 17:33:32 +08:00
/* istanbul ignore if */
2018-03-26 16:04:04 +08:00
if (process.env.NODE_ENV === 'testing') {
this.$_onPopupTransitionEnd()
}
},
$_preventScroll(isBind) {
const handler = isBind ? 'addEventListener' : 'removeEventListener'
const masker = this.$el.querySelector('.md-popup-mask')
const boxer = this.$el.querySelector('.md-popup-box')
masker && masker[handler]('touchmove', this.$_preventDefault, false)
boxer && boxer[handler]('touchmove', this.$_preventDefault, false)
this.$_preventScrollExclude(isBind)
},
$_preventScrollExclude(isBind, preventScrollExclude) {
const handler = isBind ? 'addEventListener' : 'removeEventListener'
preventScrollExclude = preventScrollExclude || this.preventScrollExclude
2018-03-26 16:04:04 +08:00
const excluder =
preventScrollExclude && typeof preventScrollExclude === 'string'
? this.$el.querySelector(preventScrollExclude)
: preventScrollExclude
excluder && excluder[handler]('touchmove', this.$_stopImmediatePropagation, false)
},
$_preventDefault(event) {
event.preventDefault()
},
$_stopImmediatePropagation(event) {
2018-04-23 17:33:32 +08:00
/* istanbul ignore next */
2018-03-26 16:04:04 +08:00
event.stopImmediatePropagation()
},
// MARK: event handler
$_onPopupTransitionStart() {
if (!this.isPopupBoxShow) {
this.$emit('beforeHide')
this.$emit('before-hide')
} else {
this.$emit('beforeShow')
this.$emit('before-show')
}
},
$_onPopupTransitionEnd() {
/* istanbul ignore next */
if (!this.isAnimation) {
return
}
/* istanbul ignore next */
if (!this.isPopupBoxShow) {
// popup hide after popup box finish animation
this.isPopupShow = false
this.$emit('hide')
} else {
this.$emit('show')
}
/* istanbul ignore next */
this.isAnimation = false
},
$_onPopupMaskClick() {
if (this.maskClosable) {
this.$_hidePopupBox()
this.$emit('maskClick')
}
2018-03-26 16:04:04 +08:00
},
},
}
</script>
<style lang="stylus">
.md-popup
&.with-mask
absolute-pos()
position fixed
z-index popup-zindex
.md-popup-box
position absolute
z-index 2
&.center .md-popup-box
absolute-pos(50%, auto, auto, 50%)
transform translate(-50%, -50%)
&.top, &.bottom, &.left, &.right
.md-popup-box
transition all .3s
&.top, &.bottom
.md-popup-box
width 100%
&.left, &.right
.md-popup-box
height 100%
&.top .md-popup-box
top 0
left 0
&.bottom .md-popup-box
bottom 0
left 0
2018-08-21 17:37:49 +08:00
border-radius popup-title-bar-radius popup-title-bar-radius 0 0
2018-03-26 16:04:04 +08:00
&.left .md-popup-box
left 0
top 0
&.right .md-popup-box
right 0
top 0
2018-09-03 16:51:17 +08:00
.md-popup-mask
absolute-pos()
position absolute
z-index 1
background-color popup-mask-bg
.md-popup-box
position fixed
z-index popup-zindex
max-width 100%
max-height 100%
overflow auto
will-change auto
.md-popup
/*
**************
* TRANSITION *
**************
*/
2018-09-03 16:51:17 +08:00
&.center
.fade-enter-active, .fade-leave-active
transition opacity .3s
.fade-enter, .fade-leave-to
opacity 0.01
.fade-zoom-enter, .fade-zoom-leave-to
opacity 0.01
transform translate(-50%, -50%) scale(0.75)
.fade-zoom-leave-active
transition all .2s
.fade-zoom-enter-active
transition all .3s
2018-03-26 16:04:04 +08:00
2018-09-03 16:51:17 +08:00
.fade-bounce-enter, .fade-bounce-leave-to
opacity 0.01
transform translate(-50%, -50%) scale(0.5)
.fade-bounce-leave-active
transition all .2s
.fade-bounce-enter-active
animation bounce-in .3s
2018-09-03 16:51:17 +08:00
.fade-slide-enter-active, .fade-slide-leave-active
transition all .3s
.fade-slide-enter, .fade-slide-leave-to
opacity 0.01
transform translate(-50%, calc(-50% - 20px)) !important
2018-03-26 16:04:04 +08:00
.slide-up-enter-active, .slide-up-leave-active, .slide-down-enter-active, .slide-down-leave-active, .bottom .show
transform translateY(0)
.slide-up-enter, .slide-up-leave-to
/* Solve the problem of hiding to show
* in the animation state of elements outside the viewport
*/
transform translateY(70%)
.slide-up-leave-active
transform translateY(100%)
.slide-down-enter, .slide-down-leave-to
transform translateY(-70%)
.slide-down-leave-active
transform translateY(-100%)
.slide-left-enter-active, .slide-left-leave-active, .slide-right-enter-active, .slide-right-leave-active
transform translateX(0)
.slide-left-enter, .slide-left-leave-to
transform translateX(70%)
.slide-left-leave-active
transform translateX(100%)
.slide-right-enter, .slide-right-leave-to
transform translateX(-70%)
.slide-right-leave-active
transform translateX(-100%)
/*
*************
* ANIMATION *
*************
*/
@keyframes bounce-in
0%
transform translate(-50%, -50%) scale(1)
1%
transform translate(-50%, -50%) scale(0.5)
45%
transform translate(-50%, -50%) scale(1.05)
80%
transform translate(-50%, -50%) scale(0.95)
100%
transform translate(-50%, -50%) scale(1)
2018-03-26 16:04:04 +08:00
</style>