index.taro.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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.zIndex != 2000) {
  137. _zIndex = Number(props.zIndex);
  138. }
  139. emit('update:visible', true);
  140. lockScroll();
  141. state.zIndex = ++_zIndex;
  142. if (props.destroyOnClose) {
  143. state.showSlot = true;
  144. }
  145. emit('open');
  146. };
  147. const close = () => {
  148. if (props.visible) {
  149. unlockScroll();
  150. emit('update:visible', false);
  151. if (props.destroyOnClose) {
  152. setTimeout(() => {
  153. state.showSlot = false;
  154. emit('close');
  155. }, +props.duration * 1000);
  156. }
  157. }
  158. };
  159. const onClick = (e: Event) => {
  160. emit('click', e);
  161. };
  162. const onClickCloseIcon = (e: Event) => {
  163. emit('click-close-icon', e);
  164. close();
  165. };
  166. const onClickOverlay = (e: Event) => {
  167. if (props.closeOnClickOverlay) {
  168. emit('click-overlay', e);
  169. close();
  170. }
  171. };
  172. const onOpened = (e: Event) => {
  173. emit('opend', e);
  174. };
  175. const onClosed = (e: Event) => {
  176. emit('closed', e);
  177. };
  178. onMounted(() => {
  179. props.transition
  180. ? (state.transitionName = props.transition)
  181. : (state.transitionName = `popup-slide-${props.position}`);
  182. props.visible && open();
  183. });
  184. onBeforeUnmount(() => {
  185. props.visible && close();
  186. });
  187. onBeforeMount(() => {
  188. if (props.visible) {
  189. unlockScroll();
  190. }
  191. });
  192. onActivated(() => {
  193. if (state.keepAlive) {
  194. emit('update:visible', true);
  195. state.keepAlive = false;
  196. }
  197. });
  198. onDeactivated(() => {
  199. if (props.visible) {
  200. close();
  201. state.keepAlive = true;
  202. }
  203. });
  204. watch(
  205. () => props.visible,
  206. (value) => {
  207. if (value) {
  208. open();
  209. } else {
  210. close();
  211. }
  212. }
  213. );
  214. watch(
  215. () => props.position,
  216. (value) => {
  217. value === 'center' ? (state.transitionName = 'popup-fade') : (state.transitionName = `popup-slide-${value}`);
  218. }
  219. );
  220. watch(
  221. () => props.closeable,
  222. (value) => {
  223. state.closed = value;
  224. }
  225. );
  226. return {
  227. ...toRefs(state),
  228. popStyle,
  229. classes,
  230. onClick,
  231. onClickCloseIcon,
  232. onClickOverlay,
  233. onOpened,
  234. onClosed
  235. };
  236. }
  237. });
  238. </script>