06ad61ef17ca96ae769357a3883f1930f1d4f4cc8db4089b2599208c0ac7cca11f50561726450118c6ba2a7b2f9215b4837babdc5455d9a5af6045c7ad98d2 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <template>
  2. <transition
  3. name="el-drawer-fade"
  4. @after-enter="afterEnter"
  5. @after-leave="afterLeave">
  6. <div
  7. class="el-drawer__wrapper"
  8. tabindex="-1"
  9. v-show="visible">
  10. <div
  11. class="el-drawer__container"
  12. :class="visible && 'el-drawer__open'"
  13. @click.self="handleWrapperClick"
  14. role="document"
  15. tabindex="-1">
  16. <div
  17. aria-modal="true"
  18. aria-labelledby="el-drawer__title"
  19. :aria-label="title"
  20. class="el-drawer"
  21. :class="[direction, customClass]"
  22. :style="isHorizontal ? `width: ${drawerSize}` : `height: ${drawerSize}`"
  23. ref="drawer"
  24. role="dialog"
  25. tabindex="-1"
  26. >
  27. <header class="el-drawer__header" id="el-drawer__title" v-if="withHeader">
  28. <slot name="title">
  29. <span role="heading" :title="title">{{ title }}</span>
  30. </slot>
  31. <button
  32. :aria-label="`close ${title || 'drawer'}`"
  33. class="el-drawer__close-btn"
  34. type="button"
  35. v-if="showClose"
  36. @click="closeDrawer">
  37. <i class="el-dialog__close el-icon el-icon-close"></i>
  38. </button>
  39. </header>
  40. <section class="el-drawer__body" v-if="rendered">
  41. <slot></slot>
  42. </section>
  43. </div>
  44. </div>
  45. </div>
  46. </transition>
  47. </template>
  48. <script>
  49. import Popup from 'element-ui/src/utils/popup';
  50. import emitter from 'element-ui/src/mixins/emitter';
  51. export default {
  52. name: 'ElDrawer',
  53. mixins: [Popup, emitter],
  54. props: {
  55. appendToBody: {
  56. type: Boolean,
  57. default: false
  58. },
  59. beforeClose: {
  60. type: Function
  61. },
  62. customClass: {
  63. type: String,
  64. default: ''
  65. },
  66. closeOnPressEscape: {
  67. type: Boolean,
  68. default: true
  69. },
  70. destroyOnClose: {
  71. type: Boolean,
  72. default: false
  73. },
  74. modal: {
  75. type: Boolean,
  76. default: true
  77. },
  78. direction: {
  79. type: String,
  80. default: 'rtl',
  81. validator(val) {
  82. return ['ltr', 'rtl', 'ttb', 'btt'].indexOf(val) !== -1;
  83. }
  84. },
  85. modalAppendToBody: {
  86. type: Boolean,
  87. default: true
  88. },
  89. showClose: {
  90. type: Boolean,
  91. default: true
  92. },
  93. size: {
  94. type: [Number, String],
  95. default: '30%'
  96. },
  97. title: {
  98. type: String,
  99. default: ''
  100. },
  101. visible: {
  102. type: Boolean
  103. },
  104. wrapperClosable: {
  105. type: Boolean,
  106. default: true
  107. },
  108. withHeader: {
  109. type: Boolean,
  110. default: true
  111. }
  112. },
  113. computed: {
  114. isHorizontal() {
  115. return this.direction === 'rtl' || this.direction === 'ltr';
  116. },
  117. drawerSize() {
  118. return typeof this.size === 'number' ? `${this.size}px` : this.size;
  119. }
  120. },
  121. data() {
  122. return {
  123. closed: false,
  124. prevActiveElement: null
  125. };
  126. },
  127. watch: {
  128. visible(val) {
  129. if (val) {
  130. this.closed = false;
  131. this.$emit('open');
  132. if (this.appendToBody) {
  133. document.body.appendChild(this.$el);
  134. }
  135. this.prevActiveElement = document.activeElement;
  136. } else {
  137. if (!this.closed) {
  138. this.$emit('close');
  139. if (this.destroyOnClose === true) {
  140. this.rendered = false;
  141. }
  142. }
  143. this.$nextTick(() => {
  144. if (this.prevActiveElement) {
  145. this.prevActiveElement.focus();
  146. }
  147. });
  148. }
  149. }
  150. },
  151. methods: {
  152. afterEnter() {
  153. this.$emit('opened');
  154. },
  155. afterLeave() {
  156. this.$emit('closed');
  157. },
  158. hide(cancel) {
  159. if (cancel !== false) {
  160. this.$emit('update:visible', false);
  161. this.$emit('close');
  162. if (this.destroyOnClose === true) {
  163. this.rendered = false;
  164. }
  165. this.closed = true;
  166. }
  167. },
  168. handleWrapperClick() {
  169. if (this.wrapperClosable) {
  170. this.closeDrawer();
  171. }
  172. },
  173. closeDrawer() {
  174. if (typeof this.beforeClose === 'function') {
  175. this.beforeClose(this.hide);
  176. } else {
  177. this.hide();
  178. }
  179. },
  180. handleClose() {
  181. // This method here will be called by PopupManger, when the `closeOnPressEscape` was set to true
  182. // pressing `ESC` will call this method, and also close the drawer.
  183. // This method also calls `beforeClose` if there was one.
  184. this.closeDrawer();
  185. }
  186. },
  187. mounted() {
  188. if (this.visible) {
  189. this.rendered = true;
  190. this.open();
  191. if (this.appendToBody) {
  192. document.body.appendChild(this.$el);
  193. }
  194. }
  195. },
  196. destroyed() {
  197. // if appendToBody is true, remove DOM node after destroy
  198. if (this.appendToBody && this.$el && this.$el.parentNode) {
  199. this.$el.parentNode.removeChild(this.$el);
  200. }
  201. }
  202. };
  203. </script>