index.taro.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <template>
  2. <view :catch-move="lockScroll">
  3. <nut-overlay
  4. v-if="overlay"
  5. :visible="visible"
  6. :close-on-click-overlay="closeOnClickOverlay"
  7. :class="overlayClass"
  8. :style="overlayStyle"
  9. :z-index="zIndex"
  10. :lock-scroll="lockScroll"
  11. :duration="duration"
  12. @click="onClickOverlay"
  13. />
  14. <Transition :name="transitionName" @after-enter="onOpened" @after-leave="onClosed">
  15. <view v-show="visible" :class="classes" :style="popStyle" @click="onClick">
  16. <div class="nutui-popup__content-wrapper" v-show="showSlot"><slot></slot></div>
  17. <view
  18. v-if="closed"
  19. @click="onClickCloseIcon"
  20. class="nutui-popup__close-icon"
  21. :class="'nutui-popup__close-icon--' + closeIconPosition"
  22. >
  23. <nut-icon :name="closeIcon" size="12px" />
  24. </view>
  25. </view>
  26. </Transition>
  27. </view>
  28. </template>
  29. <script lang="ts">
  30. import {
  31. onMounted,
  32. onBeforeMount,
  33. onBeforeUnmount,
  34. onActivated,
  35. onDeactivated,
  36. watch,
  37. computed,
  38. reactive,
  39. PropType,
  40. CSSProperties,
  41. toRefs,
  42. getCurrentInstance
  43. } from 'vue';
  44. import { useLockScroll } from '../popup/use-lock-scroll';
  45. import { overlayProps } from '../overlay/index.taro.vue';
  46. import overlay from '../overlay/index.taro.vue';
  47. import icon from '../icon/index.taro.vue';
  48. import { createComponent } from '@/packages/utils/create';
  49. const { componentName, create } = createComponent('popup');
  50. let _zIndex = 2000;
  51. export const popupProps = {
  52. ...overlayProps,
  53. position: {
  54. type: String,
  55. default: 'center'
  56. },
  57. transition: String,
  58. style: {
  59. type: Object as PropType<CSSProperties>
  60. },
  61. popClass: {
  62. type: String,
  63. default: ''
  64. },
  65. closeable: {
  66. type: Boolean,
  67. default: false
  68. },
  69. closeIconPosition: {
  70. type: String,
  71. default: 'top-right'
  72. },
  73. closeIcon: {
  74. type: String,
  75. default: 'close'
  76. },
  77. destroyOnClose: {
  78. type: Boolean,
  79. default: true
  80. },
  81. teleport: {
  82. type: [String, Element],
  83. default: 'body'
  84. },
  85. overlay: {
  86. type: Boolean,
  87. default: true
  88. },
  89. round: {
  90. type: Boolean,
  91. default: false
  92. },
  93. safeAreaInsetBottom: {
  94. type: Boolean,
  95. default: false
  96. }
  97. };
  98. export default create({
  99. children: [overlay],
  100. components: {
  101. [overlay.name]: overlay,
  102. [icon.name]: icon
  103. },
  104. props: {
  105. ...popupProps
  106. },
  107. emits: ['click', 'click-close-icon', 'open', 'close', 'opend', 'closed', 'update:visible', 'click-overlay'],
  108. setup(props, { emit }) {
  109. const state = reactive({
  110. zIndex: props.zIndex ? (props.zIndex as number) : _zIndex,
  111. showSlot: true,
  112. transitionName: `popup-fade-${props.position}`,
  113. overLayCount: 1,
  114. keepAlive: false,
  115. closed: props.closeable
  116. });
  117. const [lockScroll, unlockScroll] = useLockScroll(() => props.lockScroll);
  118. const classes = computed(() => {
  119. const prefixCls = componentName;
  120. return {
  121. [prefixCls]: true,
  122. ['round']: props.round,
  123. [`popup-${props.position}`]: true,
  124. [`popup-${props.position}--safebottom`]: props.position === 'bottom' && props.safeAreaInsetBottom,
  125. [props.popClass]: true
  126. };
  127. });
  128. const popStyle = computed(() => {
  129. return {
  130. zIndex: state.zIndex,
  131. animationDuration: props.duration ? `${props.duration}s` : 'initial',
  132. ...props.style
  133. };
  134. });
  135. const open = () => {
  136. if (!props.visible) {
  137. if (props.zIndex !== undefined) {
  138. _zIndex = Number(props.zIndex);
  139. }
  140. emit('update:visible', true);
  141. lockScroll();
  142. state.zIndex = ++_zIndex;
  143. }
  144. if (props.destroyOnClose) {
  145. state.showSlot = true;
  146. }
  147. emit('open');
  148. };
  149. const close = () => {
  150. if (props.visible) {
  151. unlockScroll();
  152. emit('update:visible', false);
  153. if (props.destroyOnClose) {
  154. setTimeout(() => {
  155. state.showSlot = false;
  156. emit('close');
  157. }, +props.duration * 1000);
  158. }
  159. }
  160. };
  161. const onClick = (e: Event) => {
  162. emit('click', e);
  163. };
  164. const onClickCloseIcon = (e: Event) => {
  165. emit('click-close-icon', e);
  166. close();
  167. };
  168. const onClickOverlay = (e: Event) => {
  169. if (props.closeOnClickOverlay) {
  170. emit('click-overlay', e);
  171. close();
  172. }
  173. };
  174. const onOpened = (e: Event) => {
  175. emit('opend', e);
  176. };
  177. const onClosed = (e: Event) => {
  178. emit('closed', e);
  179. };
  180. onMounted(() => {
  181. props.transition
  182. ? (state.transitionName = props.transition)
  183. : (state.transitionName = `popup-slide-${props.position}`);
  184. props.visible && open();
  185. });
  186. onBeforeUnmount(() => {
  187. props.visible && close();
  188. });
  189. onBeforeMount(() => {
  190. if (props.visible) {
  191. unlockScroll();
  192. }
  193. });
  194. onActivated(() => {
  195. if (state.keepAlive) {
  196. emit('update:visible', true);
  197. state.keepAlive = false;
  198. }
  199. });
  200. onDeactivated(() => {
  201. if (props.visible) {
  202. close();
  203. state.keepAlive = true;
  204. }
  205. });
  206. watch(
  207. () => props.visible,
  208. (value) => {
  209. if (value) {
  210. open();
  211. } else {
  212. close();
  213. }
  214. }
  215. );
  216. watch(
  217. () => props.position,
  218. (value) => {
  219. value === 'center' ? (state.transitionName = 'popup-fade') : (state.transitionName = `popup-slide-${value}`);
  220. }
  221. );
  222. watch(
  223. () => props.closeable,
  224. (value) => {
  225. state.closed = value;
  226. }
  227. );
  228. return {
  229. ...toRefs(state),
  230. popStyle,
  231. classes,
  232. onClick,
  233. onClickCloseIcon,
  234. onClickOverlay,
  235. onOpened,
  236. onClosed
  237. };
  238. }
  239. });
  240. </script>