瀏覽代碼

feat: add animation effects to VbenModal component

 panda7 2 月之前
父節點
當前提交
1a9b0509d5

+ 10 - 0
docs/src/components/common-ui/vben-modal.md

@@ -56,6 +56,15 @@ Modal 内的内容一般业务中,会比较复杂,所以我们可以将 moda
 
 <DemoPreview dir="demos/vben-modal/shared-data" />
 
+## 动画类型
+
+通过 `animationType` 属性可以控制弹窗的动画效果:
+
+- `slide`(默认):从顶部向下滑动进入/退出
+- `scale`:缩放淡入/淡出效果
+
+<DemoPreview dir="demos/vben-modal/animation-type" />
+
 ::: info 注意
 
 - `VbenModal` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenModal参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
@@ -112,6 +121,7 @@ const [Modal, modalApi] = useVbenModal({
 | bordered | 是否显示border | `boolean` | `false` |
 | zIndex | 弹窗的ZIndex层级 | `number` | `1000` |
 | overlayBlur | 遮罩模糊度 | `number` | - |
+| animationType | 动画类型 | `'slide' \| 'scale'` | `'slide'` |
 | submitting | 标记为提交中,锁定弹窗当前状态 | `boolean` | `false` |
 
 ::: info appendToMain

+ 36 - 0
docs/src/demos/vben-modal/animation-type/index.vue

@@ -0,0 +1,36 @@
+<script lang="ts" setup>
+import { useVbenModal, VbenButton } from '@vben/common-ui';
+
+const [SlideModal, slideModalApi] = useVbenModal({
+  animationType: 'slide',
+});
+
+const [ScaleModal, scaleModalApi] = useVbenModal({
+  animationType: 'scale',
+});
+
+function openSlideModal() {
+  slideModalApi.open();
+}
+
+function openScaleModal() {
+  scaleModalApi.open();
+}
+</script>
+
+<template>
+  <div class="space-y-4">
+    <div class="flex gap-4">
+      <VbenButton @click="openSlideModal">滑动动画</VbenButton>
+      <VbenButton @click="openScaleModal">缩放动画</VbenButton>
+    </div>
+
+    <SlideModal title="滑动动画示例" class="w-[500px]">
+      <p>这是使用滑动动画的弹窗,从顶部向下滑动进入。</p>
+    </SlideModal>
+
+    <ScaleModal title="缩放动画示例" class="w-[500px]">
+      <p>这是使用缩放动画的弹窗,以缩放淡入淡出的方式显示。</p>
+    </ScaleModal>
+  </div>
+</template>

+ 1 - 0
packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts

@@ -59,6 +59,7 @@ export class ModalApi {
       showCancelButton: true,
       showConfirmButton: true,
       title: '',
+      animationType: 'slide',
     };
 
     this.store = new Store<ModalState>(

+ 5 - 0
packages/@core/ui-kit/popup-ui/src/modal/modal.ts

@@ -5,6 +5,11 @@ import type { MaybePromise } from '@vben-core/typings';
 import type { ModalApi } from './modal-api';
 
 export interface ModalProps {
+  /**
+   * 动画类型
+   * @default 'slide'
+   */
+  animationType?: 'scale' | 'slide';
   /**
    * 是否要挂载到内容区域
    * @default false

+ 2 - 0
packages/@core/ui-kit/popup-ui/src/modal/modal.vue

@@ -94,6 +94,7 @@ const {
   submitting,
   title,
   titleTooltip,
+  animationType,
   zIndex,
 } = usePriorityValues(props, state);
 
@@ -244,6 +245,7 @@ function handleClosed() {
       :modal="modal"
       :open="state?.isOpen"
       :show-close="closable"
+      :animation-type="animationType"
       :z-index="zIndex"
       :overlay-blur="overlayBlur"
       close-class="top-3"

+ 13 - 2
packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogContent.vue

@@ -20,6 +20,7 @@ import DialogOverlay from './DialogOverlay.vue';
 const props = withDefaults(
   defineProps<
     DialogContentProps & {
+      animationType?: 'scale' | 'slide';
       appendTo?: HTMLElement | string;
       class?: ClassType;
       closeClass?: ClassType;
@@ -31,7 +32,12 @@ const props = withDefaults(
       zIndex?: number;
     }
   >(),
-  { appendTo: 'body', closeDisabled: false, showClose: true },
+  {
+    appendTo: 'body',
+    animationType: 'slide',
+    closeDisabled: false,
+    showClose: true,
+  },
 );
 const emits = defineEmits<
   DialogContentEmits & { close: []; closed: []; opened: [] }
@@ -43,6 +49,7 @@ const delegatedProps = computed(() => {
     modal: _modal,
     open: _open,
     showClose: __,
+    animationType: ___,
     ...delegated
   } = props;
 
@@ -100,7 +107,11 @@ defineExpose({
       v-bind="forwarded"
       :class="
         cn(
-          'z-popup bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-top-[48%] w-full p-6 shadow-lg outline-none sm:rounded-xl',
+          'z-popup bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 w-full p-6 shadow-lg outline-none sm:rounded-xl',
+          {
+            'data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-top-[48%]':
+              animationType === 'slide',
+          },
           props.class,
         )
       "