action-sheet/action-sheet.vue

<template>
  <div role="dialog" :class="['ion-action-sheet', modeClass, cssClass]" style="z-index: 10001;">
    <vm-backdrop
        @click="bdClick"
        :enableBackdropDismiss="enableBackdropDismiss"
        :isActive="isActive"></vm-backdrop>
    <transition name="action-sheet-fade"
        @before-enter="beforeEnter"
        @after-enter="afterEnter"
        @before-leave="beforeLeave"
        @after-leave="afterLeave">
      <div class="action-sheet-wrapper" v-show="isActive">
        <div class="action-sheet-container">
          <div class="action-sheet-group">
            <div class="action-sheet-title">
              <span>{{title}}</span>
              <div class="action-sheet-sub-title" v-if="subTitle">{{subTitle}}</div>
            </div>
            <vm-button role="action-sheet-button" :key="index" v-for="(button, index) in buttons" :disabled="button.disabled" :class="button.cssClass" @click="clickHandler(button)">
              <vm-icon class="action-sheet-icon icon" :name="button.icon" v-if="button.icon"></vm-icon>
              {{button.text}}
            </vm-button>
          </div>
          <div class="action-sheet-group">
            <vm-button role="action-sheet-button" class="action-sheet-cancel" :class="cancelButton.cssClass" :disabled="cancelButton.disabled" @click="clickHandler(cancelButton)">
              <vm-icon class="action-sheet-icon icon" :name="cancelButton.icon" v-if="cancelButton.icon"></vm-icon>
              {{cancelButton.text}}
            </vm-button>
          </div>
        </div>
      </div>
    </transition>
  </div>
</template>
<script type="text/javascript">
import {urlChange, focusOutActiveElement} from '../../util/dom'
import {isFunction, isTrueProperty, isString} from '../../util/util'
import objectAssign from 'object-assign'
import ModeMixins from '../../themes/theme.mixins'
import VmBackdrop from '../backdrop'
import VmButton from '../button'
import VmIcon from '../icon'

const NOOP = () => {}

export default {
  name: 'vm-action-sheet',
  mixins: [ModeMixins],
  components: {
    VmButton,
    VmBackdrop,
    VmIcon
  },
  data () {
    return {
      defaultOptions: {
        title: '',
        subTitle: '',
        cssClass: '',
        buttons: []
      },
      title: '',
      subTitle: '',
      cssClass: '',
      buttons: [],
      enableBackdropDismiss: true,
      dismissOnPageChange: true,

      dismissCallback: NOOP, // 关闭的回调
      presentCallback: NOOP, // 打开的回调

      cancelButton: {},
      isActive: false
    }
  },
  created () {
    let _options = objectAssign({}, this.defaultOptions, this.$options.$data)
    this.title = _options.title.trim()
    this.subTitle = _options.subTitle.trim()
    this.enableBackdropDismiss = isTrueProperty(_options.enableBackdropDismiss)
    this.dismissOnPageChange = isTrueProperty(_options.dismissOnPageChange)
    this.cssClass = _options.cssClass.trim()

    _options.buttons.forEach((button) => {
      if (isString(button)) {
        button = { text: button }
      }

      if (!button.cssClass) {
        button.cssClass = ''
      } else {
        button.cssClass = button.cssClass.trim()
      }

      if (button.role === 'cancel') {
        this.cancelButton = button
      } else {
        if (button.role === 'destructive') {
          button.cssClass = (button.cssClass + ' ' || '') + 'action-sheet-destructive'
        } else if (button.role === 'selected') {
          button.cssClass = (button.cssClass + ' ' || '') + 'action-sheet-selected'
        }
        this.buttons.push(button)
      }
    })

    if (this.dismissOnPageChange) {
      this.unreg = urlChange(() => {
        this.isActive && this.dismiss()
      })
    }
  },
  methods: {
    beforeEnter () {
      this.enabled = false // 不允许过渡中途操作
      this.$app && this.$app.setEnabled(false, 400)
    },
    afterEnter () {
      this.presentCallback()
      focusOutActiveElement()
      let focusableEle = document.querySelector('button')
      if (focusableEle) {
        focusableEle.focus()
      }
      this.enabled = true
    },
    beforeLeave () {
      this.enabled = false
      this.$app && this.$app.setEnabled(false, 400)
    },
    afterLeave () {
      this.dismissCallback()
      // 删除DOM
      this.$el.remove()
      this.enabled = true
    },
    /**
     * @function present
     * @description
     * 开启ActionSheet组件, 当开启动画过度完毕时触发 `Promise` 的 `resolve` 。
     * @param {object} options - 传入参数
     * @param {String} [options.title]                        - ActionSheet的标题
     * @param {string} [options.subTitle]                     - ActionSheet的副标题
     * @param {string} [options.cssClass]                     - 自定义样式
     * @param {Boolean} [options.enableBackdropDismiss=true]  - 允许点击backdrop关闭actionsheet
     * @param {Boolean} [options.dismissOnPageChange=true]    - 路由切换关闭组件
     * @param {Array} [options.buttons]                       - button数组,包含全部role
     * @param {Array} options.buttons.text                    - button显示文本
     * @param {Array} options.buttons.icon                    - button显示的icon的name, 具体参考Icon组件
     * @param {Array} options.buttons.role                    - 可以是cancel(关闭)/destructive(警告/删除)/null
     * @param {Array} options.buttons.handler                 - 默认是关闭组件
     * @param {Array} options.buttons.cssClass                - 自定义样式
     * @return {Promise}
     */
    present () {
      this.isActive = true
      return new Promise((resolve) => {
        this.presentCallback = resolve
      })
    },

    dismiss () {
      if (this.isActive) {
        this.isActive = false
        this.unReg && this.unReg()
        return new Promise((resolve) => {
          this.dismissCallback = resolve
        })
      } else {
        return new Promise((resolve) => {
          resolve()
        })
      }
    },

    /**
     * @function click
     * @description 点击下方按钮
     * @param {object} button Button Click Handler
     * @private
    */
    clickHandler (button) {
      if (!this.enabled) {
        return
      }
      let shouldDismiss = true
      if (isFunction(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 (this.enabled && shouldDismiss) {
        this.dismiss()
      }
    },

    bdClick () {
      if (!this.enabled || !this.enableBackdropDismiss) return
      if (this.cancelButton) {
        this.clickHandler(this.cancelButton)
      } else {
        this.dismiss()
      }
    }
  }
}
</script>

<style lang="scss">
@import "action-sheet";
@import "action-sheet.ios";
@import "action-sheet.md";
@import "../../transition/action-sheet";
</style>