init
This commit is contained in:
147
uni_modules/nutui-uni/components/popup/index.scss
Normal file
147
uni_modules/nutui-uni/components/popup/index.scss
Normal file
@@ -0,0 +1,147 @@
|
||||
@import "../overlay/index";
|
||||
|
||||
.nut-theme-dark {
|
||||
.nut-popup {
|
||||
background: $dark-background2;
|
||||
|
||||
&__close-icon {
|
||||
color: $dark-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nut-popup-slide {
|
||||
&-center-enter-active,
|
||||
&-center-leave-active {
|
||||
transition-timing-function: ease;
|
||||
transition-property: opacity;
|
||||
}
|
||||
|
||||
&-center-enter-from,
|
||||
&-center-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&-top-enter-from,
|
||||
&-top-leave-active {
|
||||
transform: translate(0, -100%);
|
||||
}
|
||||
|
||||
&-right-enter-from,
|
||||
&-right-leave-active {
|
||||
transform: translate(100%, 0);
|
||||
}
|
||||
|
||||
&-bottom-enter-from,
|
||||
&-bottom-leave-active {
|
||||
transform: translate(0, 100%);
|
||||
}
|
||||
|
||||
&-left-enter-from,
|
||||
&-left-leave-active {
|
||||
transform: translate(-100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.nut-popup--center {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
&.round {
|
||||
border-radius: $popup-border-radius;
|
||||
}
|
||||
}
|
||||
|
||||
.nut-popup--bottom {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
|
||||
&.round {
|
||||
border-radius: $popup-border-radius $popup-border-radius 0 0;
|
||||
}
|
||||
|
||||
&--safebottom {
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
}
|
||||
|
||||
.nut-popup--right {
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
&.round {
|
||||
border-radius: $popup-border-radius 0 0 $popup-border-radius;
|
||||
}
|
||||
}
|
||||
|
||||
.nut-popup--left {
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
&.round {
|
||||
border-radius: 0 $popup-border-radius $popup-border-radius 0;
|
||||
}
|
||||
}
|
||||
|
||||
.nut-popup--top {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
|
||||
&.round {
|
||||
border-radius: 0 0 $popup-border-radius $popup-border-radius;
|
||||
}
|
||||
|
||||
&--safetop {
|
||||
padding-top: var(--status-bar-height);
|
||||
padding-top: constant(safe-area-inset-top);
|
||||
padding-top: env(safe-area-inset-top);
|
||||
}
|
||||
}
|
||||
|
||||
.nut-popup {
|
||||
position: fixed;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
background-color: $white;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
&__close-icon {
|
||||
position: absolute !important;
|
||||
z-index: 1;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
color: #969799;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
&--top-left {
|
||||
top: $popup-close-icon-margin;
|
||||
left: $popup-close-icon-margin;
|
||||
}
|
||||
|
||||
&--top-right {
|
||||
top: $popup-close-icon-margin;
|
||||
right: $popup-close-icon-margin;
|
||||
}
|
||||
|
||||
&--bottom-left {
|
||||
bottom: $popup-close-icon-margin;
|
||||
left: $popup-close-icon-margin;
|
||||
}
|
||||
|
||||
&--bottom-right {
|
||||
right: $popup-close-icon-margin;
|
||||
bottom: $popup-close-icon-margin;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
uni_modules/nutui-uni/components/popup/index.ts
Normal file
2
uni_modules/nutui-uni/components/popup/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './popup'
|
||||
export * from './use-popup'
|
||||
79
uni_modules/nutui-uni/components/popup/popup.ts
Normal file
79
uni_modules/nutui-uni/components/popup/popup.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import type { ExtractPropTypes, PropType } from 'vue'
|
||||
import { CLOSE_EVENT, CLOSED_EVENT, OPEN_EVENT, OPENED_EVENT, UPDATE_VISIBLE_EVENT } from '../_constants'
|
||||
import type { Position } from '../_constants/types'
|
||||
import { commonProps, makeStringProp, truthProp } from '../_utils'
|
||||
import { overlayProps } from '../overlay/overlay'
|
||||
import type { NutAnimationName } from '../transition/types'
|
||||
|
||||
export const popupProps = {
|
||||
...overlayProps,
|
||||
...commonProps,
|
||||
/**
|
||||
* @description 弹出位置(top,bottom,left,right,center)
|
||||
*/
|
||||
position: makeStringProp<Position>('center'),
|
||||
/**
|
||||
* @description 动画名
|
||||
*/
|
||||
transition: {
|
||||
type: String as PropType<NutAnimationName>,
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* @description 自定义弹框类名
|
||||
*/
|
||||
popClass: makeStringProp(''),
|
||||
/**
|
||||
* @description 是否显示圆角
|
||||
*/
|
||||
round: Boolean,
|
||||
/**
|
||||
* @description 是否显示关闭按钮
|
||||
*/
|
||||
closeable: Boolean,
|
||||
/**
|
||||
* @description 关闭按钮图标
|
||||
*/
|
||||
closeIcon: makeStringProp('close'),
|
||||
/**
|
||||
* @description 关闭按钮位置(top-left,top-right,bottom-left,bottom-right)
|
||||
*/
|
||||
closeIconPosition: makeStringProp<'top-right' | 'bottom-right' | 'bottom-left' | 'top-left'>('top-right'),
|
||||
/**
|
||||
* @description 是否保留弹层关闭后的内容
|
||||
*/
|
||||
destroyOnClose: truthProp,
|
||||
/**
|
||||
* @description 是否显示遮罩层
|
||||
*/
|
||||
overlay: truthProp,
|
||||
/**
|
||||
* @description 是否开启 iPhone 系列全面屏底部安全区适配,仅当 `position` 为 `bottom` 时有效
|
||||
*/
|
||||
safeAreaInsetBottom: Boolean,
|
||||
/**
|
||||
* @description 是否开启 iPhone 顶部安全区适配
|
||||
*/
|
||||
safeAreaInsetTop: truthProp,
|
||||
}
|
||||
|
||||
export type PopupProps = ExtractPropTypes<typeof popupProps>
|
||||
|
||||
/* eslint-disable unused-imports/no-unused-vars */
|
||||
export const popupEmits = {
|
||||
[UPDATE_VISIBLE_EVENT]: (value: boolean) => true,
|
||||
'click-pop': (event: any) => true,
|
||||
'click-close-icon': () => true,
|
||||
'click-overlay': () => true,
|
||||
[OPEN_EVENT]: () => true,
|
||||
[OPENED_EVENT]: () => true,
|
||||
[CLOSE_EVENT]: () => true,
|
||||
[CLOSED_EVENT]: () => true,
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
'opend': () => true,
|
||||
}
|
||||
/* eslint-enable unused-imports/no-unused-vars */
|
||||
|
||||
export type PopupEmits = typeof popupEmits
|
||||
87
uni_modules/nutui-uni/components/popup/popup.vue
Normal file
87
uni_modules/nutui-uni/components/popup/popup.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import { PREFIX } from '../_constants'
|
||||
import NutIcon from '../icon/icon.vue'
|
||||
import NutOverlay from '../overlay/overlay.vue'
|
||||
import NutTransition from '../transition/transition.vue'
|
||||
import { popupEmits, popupProps } from './popup'
|
||||
import { usePopup } from './use-popup'
|
||||
|
||||
const props = defineProps(popupProps)
|
||||
|
||||
const emit = defineEmits(popupEmits)
|
||||
|
||||
const {
|
||||
classes,
|
||||
popStyle,
|
||||
innerIndex,
|
||||
showSlot,
|
||||
transitionName,
|
||||
onClick,
|
||||
onClickCloseIcon,
|
||||
onClickOverlay,
|
||||
onOpened,
|
||||
onClosed,
|
||||
} = usePopup(props, emit)
|
||||
|
||||
const innerDuration = computed(() => {
|
||||
return Number(props.duration)
|
||||
})
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
const componentName = `${PREFIX}-popup`
|
||||
|
||||
export default defineComponent({
|
||||
name: componentName,
|
||||
options: {
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NutOverlay
|
||||
v-if="props.overlay"
|
||||
:overlay-class="props.overlayClass"
|
||||
:overlay-style="props.overlayStyle"
|
||||
:visible="props.visible"
|
||||
:z-index="innerIndex"
|
||||
:duration="innerDuration"
|
||||
:lock-scroll="props.lockScroll"
|
||||
:close-on-click-overlay="props.closeOnClickOverlay"
|
||||
:destroy-on-close="props.destroyOnClose"
|
||||
@click="onClickOverlay"
|
||||
/>
|
||||
|
||||
<NutTransition
|
||||
:custom-class="classes"
|
||||
:custom-style="popStyle"
|
||||
:name="transitionName"
|
||||
:show="props.visible"
|
||||
:duration="innerDuration"
|
||||
:destroy-on-close="props.destroyOnClose"
|
||||
@after-enter="onOpened"
|
||||
@after-leave="onClosed"
|
||||
@click="onClick"
|
||||
>
|
||||
<slot v-if="showSlot" />
|
||||
|
||||
<view
|
||||
v-if="props.closeable"
|
||||
class="nut-popup__close-icon"
|
||||
:class="`nut-popup__close-icon--${props.closeIconPosition}`"
|
||||
@click="onClickCloseIcon"
|
||||
>
|
||||
<slot name="closeIcon">
|
||||
<NutIcon name="close" height="12px" />
|
||||
</slot>
|
||||
</view>
|
||||
</NutTransition>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "./index";
|
||||
</style>
|
||||
132
uni_modules/nutui-uni/components/popup/use-popup.ts
Normal file
132
uni_modules/nutui-uni/components/popup/use-popup.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import type { SetupContext } from 'vue'
|
||||
import { computed, onMounted, reactive, toRefs, watch } from 'vue'
|
||||
import {
|
||||
animationName,
|
||||
CLOSE_EVENT,
|
||||
CLOSED_EVENT,
|
||||
OPEN_EVENT,
|
||||
OPENED_EVENT,
|
||||
PREFIX,
|
||||
UPDATE_VISIBLE_EVENT,
|
||||
} from '../_constants'
|
||||
import { useGlobalZIndex } from '../_hooks'
|
||||
import { getMainClass, getMainStyle } from '../_utils'
|
||||
import type { NutAnimationName } from '../transition'
|
||||
import type { PopupEmits, PopupProps } from './popup'
|
||||
|
||||
const componentName = `${PREFIX}-popup`
|
||||
|
||||
export function usePopup(props: PopupProps, emit: SetupContext<PopupEmits>['emit']) {
|
||||
const state = reactive({
|
||||
innerVisible: false,
|
||||
innerIndex: props.zIndex,
|
||||
showSlot: true,
|
||||
})
|
||||
|
||||
const classes = computed(() => {
|
||||
return getMainClass(props, componentName, {
|
||||
round: props.round,
|
||||
[`nut-popup--${props.position}`]: true,
|
||||
[`nut-popup--${props.position}--safebottom`]: props.position === 'bottom' && props.safeAreaInsetBottom,
|
||||
[`nut-popup--${props.position}--safetop`]: props.position === 'top' && props.safeAreaInsetTop,
|
||||
[props.popClass]: true,
|
||||
})
|
||||
})
|
||||
|
||||
const popStyle = computed(() => {
|
||||
return getMainStyle(props, {
|
||||
zIndex: state.innerIndex,
|
||||
transitionDuration: `${props.duration}ms`,
|
||||
})
|
||||
})
|
||||
|
||||
const transitionName = computed<NutAnimationName>(() => {
|
||||
return props.transition ? props.transition : `${animationName[props.position]}`
|
||||
})
|
||||
|
||||
const open = () => {
|
||||
if (state.innerVisible)
|
||||
return
|
||||
|
||||
state.innerIndex = props.zIndex !== undefined ? props.zIndex : useGlobalZIndex()
|
||||
|
||||
state.innerVisible = true
|
||||
emit(UPDATE_VISIBLE_EVENT, true)
|
||||
|
||||
state.showSlot = true
|
||||
|
||||
emit(OPEN_EVENT)
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
if (!state.innerVisible)
|
||||
return
|
||||
|
||||
state.innerVisible = false
|
||||
emit(UPDATE_VISIBLE_EVENT, false)
|
||||
|
||||
emit(CLOSE_EVENT)
|
||||
}
|
||||
|
||||
const onClick = (e: any) => {
|
||||
emit('click-pop', e)
|
||||
}
|
||||
|
||||
const onClickCloseIcon = (e: any) => {
|
||||
e.stopPropagation()
|
||||
|
||||
emit('click-close-icon')
|
||||
|
||||
close()
|
||||
}
|
||||
|
||||
const onClickOverlay = () => {
|
||||
emit('click-overlay')
|
||||
|
||||
if (props.closeOnClickOverlay)
|
||||
close()
|
||||
}
|
||||
|
||||
const onOpened = () => {
|
||||
emit(OPENED_EVENT)
|
||||
emit('opend')
|
||||
}
|
||||
|
||||
const onClosed = () => {
|
||||
emit(CLOSED_EVENT)
|
||||
|
||||
state.showSlot = !props.destroyOnClose
|
||||
}
|
||||
|
||||
const applyVisible = (visible: boolean) => {
|
||||
if (visible && !state.innerVisible) {
|
||||
open()
|
||||
}
|
||||
|
||||
if (!visible && state.innerVisible) {
|
||||
state.innerVisible = false
|
||||
|
||||
emit(CLOSE_EVENT)
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.visible, (value) => {
|
||||
applyVisible(value)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
applyVisible(props.visible)
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
popStyle,
|
||||
transitionName,
|
||||
classes,
|
||||
onClick,
|
||||
onClickCloseIcon,
|
||||
onClickOverlay,
|
||||
onOpened,
|
||||
onClosed,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user