<template>
<div class="vm-pop-sheet" :class="[modeClass,{'vm-pop-sheet-visible':isVisible}]">
<vm-backdrop :bdClick="bdClick" :enableBackdropDismiss="enableBackdropDismiss"
:isActive="isActive && showBackdrop"></vm-backdrop>
<transition
name="pop-sheet"
@before-enter="beforeEnter"
@after-enter="afterEnter"
@before-leave="beforeLeave"
@after-leave="afterLeave">
<div class="sheet-wrapper" v-show="isActive" @touchmove="onTouchMoveHandler($event)">
<div class="sheet-container">
<slot></slot>
</div>
</div>
</transition>
</div>
</template>
<script type="text/javascript">
/**
* @component PopSheet
* @description
*
* ## 弹出窗组件 / PopSheet
*
* ### 介绍
*
* PopSheet组件是一个模板组件, 自身只提供弹出载体, 其余业务部分通过slot传入, 且组件本身不对slot内容不作处理, 样式也由业务自己确定, 组件中的内容将自动在页面居中.
*
* 组件开闭通过ref获取组件实例并通过下面两个方法操作:
* - present() 开启
* - dismiss() 关闭
*
* ### 如何引入
* ```
* // 引入
* import PopSheet from 'vimo/lib/pop-sheet'
* // 安装
* Vue.component(PopSheet.name, PopSheet)
* // 或者
* export default{
* components: {
* PopSheet
* }
* }
* ```
*
* ### 关于slot
*
* 组件必须具有slot="fixed"属性, 表示固定在页面上. 如果没有的话, 页面滚动样式会产生错误.
*
* ### 如何开启
*
* 通过ref获取组件实例, 之后调用present方法开启, 调用dismiss方法关闭.
*
* ```
* ...
* computed: {
* popSheetComponent () {
* return this.$refs.popSheet
* }
* }
* methods: {
* openPopSheet () {
* return this.popSheetComponent.present()
* },
* closePopSheet () {
* return this.popSheetComponent.dismiss()
* }
* }
* ...
* ```
*
* @props {Boolean} [enableBackdropDismiss=true] - 点击背景关闭组件
* @props {String} [mode='ios'] - 模式
* @props {Boolean} [dismissOnPageChange=true] - 页面切换关闭组件
* @props {boolean} [showBackdrop=true] - 是否显示黑色背景
*
* @demo #/pop-sheet
* @usage
* <vm-pop-sheet ref="popSheet" slot="fixed">
* <section class="popSheet">
* <h2>TITLE</h2>
* <h5>这是内容, 宽度高度自定义</h5>
* <p>
* Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consectetur, eaque earum eum
* ex expedita facere fugit ipsa ipsum minima nemo nostrum, pariatur placeat qui quis quod
* quos repellendus tempora unde.</p>
* <vm-list>
* <vm-item>
* <vm-label>名称</vm-label>
* <vm-input v-model="name" type="text" placeholder="请输入用户名" clear-input></vm-input>
* </vm-item>
* <vm-item>
* <vm-label>密码</vm-label>
* <vm-input v-model="password" type="password" placeholder="请输入密码" clear-input></vm-input>
* </vm-item>
* </vm-list>
* <vm-button block @click="closePopSheet">确认</vm-button>
* </section>
* </vm-pop-sheet>
*
* */
import { urlChange } from '../../util/util'
import ThemeMixins from '../../themes/theme.mixins'
import VmBackdrop from '../backdrop/backdrop.vue'
const NOOP = () => {}
export default {
name: 'vm-pop-sheet',
mixins: [ThemeMixins],
components: {
VmBackdrop
},
props: {
enableBackdropDismiss: {
type: Boolean,
default () { return true }
},
dismissOnPageChange: {
type: Boolean,
default () { return true }
},
showBackdrop: {
type: Boolean,
default: true
}
},
data () {
return {
/**
* @desc
* ActionSheet State
* 属性 | 描述 | 未开启 | 'Present动画'开始 | 'Present动画'结束 | 'Dismiss动画'开始 | 'Dismiss动画'结束 | 未开启
* ----------|------------------|--------|-------------------|-------------------|-------------------|-------------------|--------
* isActive | 开启关闭状态 | false | true | true | true | false | false
* enabled | 是否在过渡动画中 | true | false | true | true | false | true
* isVisible | 判断是否在可视区 | false | true | true | true | true | false
* @private
* */
isActive: false, // Sheet 开启关闭状态, 显示动画开启 -> 触发关闭dismiss
isVisible: false, // 是否处在显示阶段, 显示动画开启 -> 关闭动画完全关闭
enabled: true, // 是否在过渡态的状态判断,如果在动画中则为false (补漏的)
// promise
presentCallback: NOOP,
dismissCallback: NOOP,
unReg: null // 页面变化的解绑函数
}
},
methods: {
/**
* ActionSheet Animate Hooks
* @private
* */
beforeEnter () {
this.enabled = false // 不允许过渡中途操作
this.isVisible = true
this.$app && this.$app.setEnabled(false, 400)
},
afterEnter () {
this.presentCallback()
this.focusOutActiveElement()
let focusableEle = document.querySelector('button')
if (focusableEle) {
focusableEle.focus()
}
this.enabled = true
this.isVisible = true
},
beforeLeave () {
this.enabled = false
this.isVisible = true
this.$app && this.$app.setEnabled(false, 400)
},
afterLeave () {
this.dismissCallback()
this.enabled = true
this.isVisible = false
},
/**
* ActionSheet启动之前去除focus效果,因为存在键盘
* @private
* */
focusOutActiveElement () {
// only button?
const activeElement = document.activeElement
activeElement && activeElement.blur && activeElement.blur()
},
/**
* @function bdClick
* @description
* 点击backdrop,关闭actionsheet
* @private
*/
bdClick () {
if (this.enableBackdropDismiss) {
this.dismiss()
}
},
/**
* @function click
* @description
* 点击下方按钮
* @param {object} button Button Click Handler
* @private
*/
click (button) {
let shouldDismiss = true
if (button.handler) {
// a handler has been provided, execute it
if (button.handler() === false) {
// if the return value of the handler is false then do not dismiss
shouldDismiss = false
}
}
// 当前不是在过渡动画中(dismissing中),
// 如果是在dismissing中,则意味着正在关闭,
// 这里不必进行
if (shouldDismiss) {
this.dismiss()
}
},
/**
* @function present
* @description
* 打开ActionSheet
* @returns {Promise} 结果返回Promise, 当动画完毕后执行resolved
*/
present () {
this.isActive = true
return new Promise((resolve) => { this.presentCallback = resolve })
},
/**
* @function dismiss
* @description
* 关闭ActionSheet, 如果组件还在动画中, 则执行dismiss没有动作
* @return {Promise} 结果返回Promise, 当动画完毕后执行resolved
* */
dismiss () {
if (!this.enabled) {
return new Promise((resolve) => { resolve() })
}
if (this.isActive) {
this.isActive = false // 动起来
this.unReg && this.unReg()
return new Promise((resolve) => { this.dismissCallback = resolve })
} else {
return new Promise((resolve) => { resolve() })
}
},
onTouchMoveHandler ($event) {
$event.preventDefault()
$event.stopPropagation()
}
},
created () {
// mounted before data ready, so no need to judge the `dismissOnPageChange` value
if (this.dismissOnPageChange) {
this.unReg = urlChange(() => {
this.isActive && this.dismiss()
})
}
}
}
</script>
<style lang="scss">
@import "pop-sheet";
@import "pop-sheet.ios";
@import "pop-sheet.md";
</style>