blob: 4aef86ebf599372c0bd55beea9aac8f23df66065 [file] [log] [blame]
<!-- CopyRight (C) 2017-2022 WeexUI Group Holding Limited. -->
<!-- Created by git@zwwill on 18/02/08. -->
<!--A popover box with customized contents.-->
<template>
<div class="wrapper">
<div class="g-cover"
ref="wxc-cover"
v-if="show"
@click="hideAction"
:style="coverStyle"></div>
<div ref="wxc-popover"
class="g-popover"
v-if="show && buttons.length"
:style="contentStyle">
<div class="u-popover-arrow" :style="arrowStyle"></div>
<div class="u-popover-inner">
<div :class="['i-btn',i === buttons.length-1 ? 'i-btn-noborder' : '']"
v-for="(item,i) in buttons"
:key="i"
@click="wxcButtonClicked(i,item.key)">
<image :src="item.icon"
v-if="item.icon"
class="btn-icon"></image>
<text class="btn-text" :style="textStyle">{{item.text}}</text>
</div>
</div>
</div>
</div>
</template>
<script>
const animation = weex.requireModule('animation');
export default {
props: {
buttons: {
type: Array,
default: []
},
position: {
type: Object,
default: () => ({
x: 0,
y: 0
})
},
arrowPosition: {
type: Object,
default: () => ({
pos: 'top',
x: 0,
y: 0
})
},
coverColor: {
type: String,
default: 'rgba(0, 0, 0, 0.4)'
},
hasAnimation: {
type: Boolean,
default: true
},
textStyle: {
type: Object,
default: () => ({})
}
},
data: () => ({
show: false,
showIn: false
}),
computed: {
coverStyle () {
return this.coverColor ? { backgroundColor: this.coverColor, opacity: this.hasAnimation || !this.showIn ? '0' : '1' } : '';
},
transformOrigin () {
const { x = 0, y = 0, pos = 'top' } = this.arrowPosition;
let _origins = [];
switch (pos) {
case 'top':
case 'bottom':
_origins = [x < 0 ? 'right' : 'left', pos];
break;
case 'left':
case 'right':
_origins = [pos, y < 0 ? 'bottom' : 'top'];
break;
}
return _origins.join(' ');
},
contentTransform () {
const { pos = 'top' } = this.arrowPosition;
let { x = 0, y = 0 } = this.arrowPosition;
const _translates = ['scale(0)'];
if (x >= 0 && x < 22) {
x = 22;
} else if (x < 0 && x > -22) {
x = -22;
}
if (y >= 0 && y < 22) {
y = 22;
} else if (y < 0 && y > -22) {
y = -22;
}
switch (pos) {
case 'top':
case 'bottom':
_translates[1] = `translateX(${x < 0 ? (x - 15) : (x + 15)}px)`;
break;
case 'left':
case 'right':
_translates[1] = `translateY(${y < 0 ? (y - 15) : (y + 15)}px)`
break;
}
return _translates.join(' ');
},
contentStyle () {
const { x = 0, y = 0 } = this.position;
const style = {};
x < 0 ? (style.right = `${-x}px`) : (style.left = `${x}px`);
y < 0 ? (style.bottom = `${-y}px`) : (style.top = `${y}px`);
style.opacity = this.hasAnimation || !this.showIn ? '0' : '1';
style.transform = this.hasAnimation || !this.showIn ? this.contentTransform : 'scale(1)';
style.transformOrigin = this.transformOrigin;
return style;
},
arrowStyle () {
let { x = 0, y = 0 } = this.arrowPosition;
const { pos = 'top' } = this.arrowPosition;
const style = {};
switch (pos) {
case 'top':
style.top = '6px';
case 'bottom': //eslint-disable-line
!style.top && (style.bottom = '6px');
style.transform = 'scaleX(0.8) rotate(45deg)';
if (x >= 0 && x < 22) {
x = 22;
} else if (x < 0 && x > -22) {
x = -22;
}
x < 0 ? (style.right = `${-x}px`) : (style.left = `${x}px`);
break;
case 'left':
style.left = '6px';
case 'right': //eslint-disable-line
!style.left && (style.right = '6px');
style.transform = 'scaleY(0.8) rotate(45deg)';
if (y >= 0 && y < 22) {
y = 22;
} else if (y < 0 && y > -22) {
y = -22;
}
y < 0 ? (style.bottom = `${-y}px`) : (style.top = `${y}px`);
break;
default:
break;
}
return style;
}
},
methods: {
wxcPopoverShow () {
if (this.animationLock) {
return;
}
this.show = true;
if (this.hasAnimation) {
setTimeout(() => this.wxcPopoverAnimationShow(), 40)
} else {
setTimeout(() => (this.showIn = true), 40);
}
},
/**
* smooth in
**/
wxcPopoverAnimationShow () {
const popoverEl = this.$refs['wxc-popover'];
const coverEl = this.$refs['wxc-cover'];
if (!coverEl || !popoverEl) {
return;
}
this.setAnimationLock();
let a1End = false;
let a2End = false;
animation.transition(popoverEl, {
styles: {
opacity: 1,
transform: 'scale(1)',
transformOrigin: this.transformOrigin
},
delay: 0,
duration: 250,
timingFunction: 'ease-out'
}, (e) => {
a1End = true;
if (a1End && a2End) {
this.animationLock = false
}
});
animation.transition(coverEl, {
styles: {
opacity: 1
},
delay: 0,
duration: 250,
timingFunction: 'ease-in'
}, (e) => {
a2End = true;
if (a1End && a2End) {
this.animationLock = false
}
});
},
wxcButtonClicked (index, key) {
if (this.animationLock) {
return;
}
this.$emit('wxcPopoverButtonClicked', { key, index });
this.hideAction();
},
/**
* 隐藏操作
*/
hideAction () {
if (this.animationLock) {
return;
}
if (this.hasAnimation) {
this.setAnimationLock()
const popoverEl = this.$refs['wxc-popover'];
const coverEl = this.$refs['wxc-cover'];
if (!popoverEl || !coverEl) {
return;
}
let a1End = false;
let a2End = false;
animation.transition(popoverEl, {
styles: {
opacity: 0,
transform: this.contentTransform,
transformOrigin: this.transformOrigin
},
duration: 250
}, () => {
a1End = true;
if (a1End && a2End) {
this.show = false;
this.showIn = false;
this.animationLock = false
}
});
animation.transition(coverEl, {
styles: {
opacity: 0
},
duration: 250
}, () => {
a2End = true;
if (a1End && a2End) {
this.show = false;
this.showIn = false;
this.animationLock = false
}
});
} else {
this.show = false;
this.showIn = false;
}
},
/**
* 设置动画锁
*/
setAnimationLock () {
this.animationLock = true;
}
}
};
</script>
<style scoped>
.wrapper{
z-index: 999;
}
.g-cover {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
z-index: 1;
}
.g-popover {
position: fixed;
padding:15px;
z-index: 10;
}
.u-popover-arrow {
position: absolute;
border-radius: 4px;
width: 30px;
height: 30px;
background-color: #ffffff;
}
.u-popover-inner {
border-radius: 10px;
background-color: #ffffff;
}
.i-btn {
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-left: 20px;
margin-right: 20px;
padding-left: 20px;
padding-right: 20px;
border-bottom-width: 1px;
border-bottom-color: #dddddd;
}
.i-btn-noborder {
border-bottom-color: #ffffff;
}
.btn-icon {
width: 32px;
height: 32px;
margin-right: 16px;
}
.btn-text {
flex: 1;
height: 80px;
font-size: 30px;
line-height: 80px;
}
.text-align-center {
text-align: center;
}
</style>