action-sheet/action-sheet.vue

  1. <template>
  2. <div role="dialog" :class="['ion-action-sheet', modeClass, cssClass]" style="z-index: 10001;">
  3. <vm-backdrop
  4. @click="bdClick"
  5. :enableBackdropDismiss="enableBackdropDismiss"
  6. :isActive="isActive"></vm-backdrop>
  7. <transition name="action-sheet-fade"
  8. @before-enter="beforeEnter"
  9. @after-enter="afterEnter"
  10. @before-leave="beforeLeave"
  11. @after-leave="afterLeave">
  12. <div class="action-sheet-wrapper" v-show="isActive">
  13. <div class="action-sheet-container">
  14. <div class="action-sheet-group">
  15. <div class="action-sheet-title">
  16. <span>{{title}}</span>
  17. <div class="action-sheet-sub-title" v-if="subTitle">{{subTitle}}</div>
  18. </div>
  19. <vm-button role="action-sheet-button" :key="index" v-for="(button, index) in buttons" :disabled="button.disabled" :class="button.cssClass" @click="clickHandler(button)">
  20. <vm-icon class="action-sheet-icon icon" :name="button.icon" v-if="button.icon"></vm-icon>
  21. {{button.text}}
  22. </vm-button>
  23. </div>
  24. <div class="action-sheet-group">
  25. <vm-button role="action-sheet-button" class="action-sheet-cancel" :class="cancelButton.cssClass" :disabled="cancelButton.disabled" @click="clickHandler(cancelButton)">
  26. <vm-icon class="action-sheet-icon icon" :name="cancelButton.icon" v-if="cancelButton.icon"></vm-icon>
  27. {{cancelButton.text}}
  28. </vm-button>
  29. </div>
  30. </div>
  31. </div>
  32. </transition>
  33. </div>
  34. </template>
  35. <script type="text/javascript">
  36. import {urlChange, focusOutActiveElement} from '../../util/dom'
  37. import {isFunction, isTrueProperty, isString} from '../../util/util'
  38. import objectAssign from 'object-assign'
  39. import ModeMixins from '../../themes/theme.mixins'
  40. import VmBackdrop from '../backdrop'
  41. import VmButton from '../button'
  42. import VmIcon from '../icon'
  43. const NOOP = () => {}
  44. export default {
  45. name: 'vm-action-sheet',
  46. mixins: [ModeMixins],
  47. components: {
  48. VmButton,
  49. VmBackdrop,
  50. VmIcon
  51. },
  52. data () {
  53. return {
  54. defaultOptions: {
  55. title: '',
  56. subTitle: '',
  57. cssClass: '',
  58. buttons: []
  59. },
  60. title: '',
  61. subTitle: '',
  62. cssClass: '',
  63. buttons: [],
  64. enableBackdropDismiss: true,
  65. dismissOnPageChange: true,
  66. dismissCallback: NOOP, // 关闭的回调
  67. presentCallback: NOOP, // 打开的回调
  68. cancelButton: {},
  69. isActive: false
  70. }
  71. },
  72. created () {
  73. let _options = objectAssign({}, this.defaultOptions, this.$options.$data)
  74. this.title = _options.title.trim()
  75. this.subTitle = _options.subTitle.trim()
  76. this.enableBackdropDismiss = isTrueProperty(_options.enableBackdropDismiss)
  77. this.dismissOnPageChange = isTrueProperty(_options.dismissOnPageChange)
  78. this.cssClass = _options.cssClass.trim()
  79. _options.buttons.forEach((button) => {
  80. if (isString(button)) {
  81. button = { text: button }
  82. }
  83. if (!button.cssClass) {
  84. button.cssClass = ''
  85. } else {
  86. button.cssClass = button.cssClass.trim()
  87. }
  88. if (button.role === 'cancel') {
  89. this.cancelButton = button
  90. } else {
  91. if (button.role === 'destructive') {
  92. button.cssClass = (button.cssClass + ' ' || '') + 'action-sheet-destructive'
  93. } else if (button.role === 'selected') {
  94. button.cssClass = (button.cssClass + ' ' || '') + 'action-sheet-selected'
  95. }
  96. this.buttons.push(button)
  97. }
  98. })
  99. if (this.dismissOnPageChange) {
  100. this.unreg = urlChange(() => {
  101. this.isActive && this.dismiss()
  102. })
  103. }
  104. },
  105. methods: {
  106. beforeEnter () {
  107. this.enabled = false // 不允许过渡中途操作
  108. this.$app && this.$app.setEnabled(false, 400)
  109. },
  110. afterEnter () {
  111. this.presentCallback()
  112. focusOutActiveElement()
  113. let focusableEle = document.querySelector('button')
  114. if (focusableEle) {
  115. focusableEle.focus()
  116. }
  117. this.enabled = true
  118. },
  119. beforeLeave () {
  120. this.enabled = false
  121. this.$app && this.$app.setEnabled(false, 400)
  122. },
  123. afterLeave () {
  124. this.dismissCallback()
  125. // 删除DOM
  126. this.$el.remove()
  127. this.enabled = true
  128. },
  129. /**
  130. * @function present
  131. * @description
  132. * 开启ActionSheet组件, 当开启动画过度完毕时触发 `Promise` 的 `resolve` 。
  133. * @param {object} options - 传入参数
  134. * @param {String} [options.title] - ActionSheet的标题
  135. * @param {string} [options.subTitle] - ActionSheet的副标题
  136. * @param {string} [options.cssClass] - 自定义样式
  137. * @param {Boolean} [options.enableBackdropDismiss=true] - 允许点击backdrop关闭actionsheet
  138. * @param {Boolean} [options.dismissOnPageChange=true] - 路由切换关闭组件
  139. * @param {Array} [options.buttons] - button数组,包含全部role
  140. * @param {Array} options.buttons.text - button显示文本
  141. * @param {Array} options.buttons.icon - button显示的icon的name, 具体参考Icon组件
  142. * @param {Array} options.buttons.role - 可以是cancel(关闭)/destructive(警告/删除)/null
  143. * @param {Array} options.buttons.handler - 默认是关闭组件
  144. * @param {Array} options.buttons.cssClass - 自定义样式
  145. * @return {Promise}
  146. */
  147. present () {
  148. this.isActive = true
  149. return new Promise((resolve) => {
  150. this.presentCallback = resolve
  151. })
  152. },
  153. dismiss () {
  154. if (this.isActive) {
  155. this.isActive = false
  156. this.unReg && this.unReg()
  157. return new Promise((resolve) => {
  158. this.dismissCallback = resolve
  159. })
  160. } else {
  161. return new Promise((resolve) => {
  162. resolve()
  163. })
  164. }
  165. },
  166. /**
  167. * @function click
  168. * @description 点击下方按钮
  169. * @param {object} button Button Click Handler
  170. * @private
  171. */
  172. clickHandler (button) {
  173. if (!this.enabled) {
  174. return
  175. }
  176. let shouldDismiss = true
  177. if (isFunction(button.handler)) {
  178. // a handler has been provided, execute it
  179. if (button.handler() === false) {
  180. // if the return value of the handler is false then do not dismiss
  181. shouldDismiss = false
  182. }
  183. }
  184. // 当前不是在过渡动画中(dismissing中),
  185. // 如果是在dismissing中,则意味着正在关闭,
  186. // 这里不必进行
  187. if (this.enabled && shouldDismiss) {
  188. this.dismiss()
  189. }
  190. },
  191. bdClick () {
  192. if (!this.enabled || !this.enableBackdropDismiss) return
  193. if (this.cancelButton) {
  194. this.clickHandler(this.cancelButton)
  195. } else {
  196. this.dismiss()
  197. }
  198. }
  199. }
  200. }
  201. </script>
  202. <style lang="scss">
  203. @import "action-sheet";
  204. @import "action-sheet.ios";
  205. @import "action-sheet.md";
  206. @import "../../transition/action-sheet";
  207. </style>