feat: modal&drawer support appendToMain and zIndex (#5092)

* feat: modal/drawer support append to main content

* feat: modal zIndex support

* fix: drawer prop define

* chore: type

* fix: modal/drawer position fixed while append to body

* docs: typo

* chore: add full-width drawer in content area

* chore: remove unnecessary class
This commit is contained in:
Netfan
2024-12-10 17:37:06 +08:00
committed by GitHub
parent 018ddc75c6
commit e419b03cab
22 changed files with 271 additions and 31 deletions

View File

@@ -7,6 +7,11 @@ import type { Component, Ref } from 'vue';
export type DrawerPlacement = 'bottom' | 'left' | 'right' | 'top';
export interface DrawerProps {
/**
* 是否挂载到内容区域
* @default false
*/
appendToMain?: boolean;
/**
* 取消按钮文字
*/
@@ -59,12 +64,12 @@ export interface DrawerProps {
* 弹窗头部样式
*/
headerClass?: ClassType;
/**
* 弹窗是否显示
* @default false
*/
loading?: boolean;
/**
* 是否显示遮罩
* @default true
@@ -74,12 +79,12 @@ export interface DrawerProps {
* 是否自动聚焦
*/
openAutoFocus?: boolean;
/**
* 抽屉位置
* @default right
*/
placement?: DrawerPlacement;
/**
* 是否显示取消按钮
* @default true
@@ -98,6 +103,10 @@ export interface DrawerProps {
* 弹窗标题提示
*/
titleTooltip?: string;
/**
* 抽屉层级
*/
zIndex?: number;
}
export interface DrawerState extends DrawerProps {

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
import type { DrawerProps, ExtendedDrawerApi } from './drawer';
import { provide, ref, useId, watch } from 'vue';
import { computed, provide, ref, useId, watch } from 'vue';
import {
useIsMobile,
@@ -23,6 +23,7 @@ import {
VbenLoading,
VisuallyHidden,
} from '@vben-core/shadcn-ui';
import { ELEMENT_ID_MAIN_CONTENT } from '@vben-core/shared/constants';
import { globalShareState } from '@vben-core/shared/global-state';
import { cn } from '@vben-core/shared/utils';
@@ -31,7 +32,9 @@ interface Props extends DrawerProps {
}
const props = withDefaults(defineProps<Props>(), {
appendToMain: false,
drawerApi: undefined,
zIndex: 1000,
});
const components = globalShareState.getComponents();
@@ -46,6 +49,7 @@ const { isMobile } = useIsMobile();
const state = props.drawerApi?.useStore?.();
const {
appendToMain,
cancelText,
class: drawerClass,
closable,
@@ -67,6 +71,7 @@ const {
showConfirmButton,
title,
titleTooltip,
zIndex,
} = usePriorityValues(props, state);
watch(
@@ -110,6 +115,10 @@ function handleFocusOutside(e: Event) {
e.preventDefault();
e.stopPropagation();
}
const getAppendTo = computed(() => {
return appendToMain.value ? `#${ELEMENT_ID_MAIN_CONTENT}` : undefined;
});
</script>
<template>
<Sheet
@@ -118,6 +127,7 @@ function handleFocusOutside(e: Event) {
@update:open="() => drawerApi?.close()"
>
<SheetContent
:append-to="getAppendTo"
:class="
cn('flex w-[520px] flex-col', drawerClass, {
'!w-full': isMobile || placement === 'bottom' || placement === 'top',
@@ -127,6 +137,7 @@ function handleFocusOutside(e: Event) {
:modal="modal"
:open="state?.isOpen"
:side="placement"
:z-index="zIndex"
@close-auto-focus="handleFocusOutside"
@escape-key-down="escapeKeyDown"
@focus-outside="handleFocusOutside"

View File

@@ -3,6 +3,11 @@ import type { ModalApi } from './modal-api';
import type { Component, Ref } from 'vue';
export interface ModalProps {
/**
* 是否要挂载到内容区域
* @default false
*/
appendToMain?: boolean;
/**
* 是否显示边框
* @default false
@@ -12,7 +17,6 @@ export interface ModalProps {
* 取消按钮文字
*/
cancelText?: string;
/**
* 是否居中
* @default false
@@ -20,6 +24,7 @@ export interface ModalProps {
centered?: boolean;
class?: string;
/**
* 是否显示右上角的关闭按钮
* @default true
@@ -112,6 +117,10 @@ export interface ModalProps {
* 弹窗标题提示
*/
titleTooltip?: string;
/**
* 弹窗层级
*/
zIndex?: number;
}
export interface ModalState extends ModalProps {

View File

@@ -22,6 +22,7 @@ import {
VbenLoading,
VisuallyHidden,
} from '@vben-core/shadcn-ui';
import { ELEMENT_ID_MAIN_CONTENT } from '@vben-core/shared/constants';
import { globalShareState } from '@vben-core/shared/global-state';
import { cn } from '@vben-core/shared/utils';
@@ -32,6 +33,7 @@ interface Props extends ModalProps {
}
const props = withDefaults(defineProps<Props>(), {
appendToMain: false,
modalApi: undefined,
});
@@ -52,6 +54,7 @@ const { isMobile } = useIsMobile();
const state = props.modalApi?.useStore?.();
const {
appendToMain,
bordered,
cancelText,
centered,
@@ -78,6 +81,7 @@ const {
showConfirmButton,
title,
titleTooltip,
zIndex,
} = usePriorityValues(props, state);
const shouldFullscreen = computed(
@@ -161,6 +165,9 @@ function handleFocusOutside(e: Event) {
e.preventDefault();
e.stopPropagation();
}
const getAppendTo = computed(() => {
return appendToMain.value ? `#${ELEMENT_ID_MAIN_CONTENT}` : undefined;
});
</script>
<template>
<Dialog
@@ -170,6 +177,7 @@ function handleFocusOutside(e: Event) {
>
<DialogContent
ref="contentRef"
:append-to="getAppendTo"
:class="
cn(
'left-0 right-0 top-[10vh] mx-auto flex max-h-[80%] w-[520px] flex-col p-0 sm:rounded-[var(--radius)]',
@@ -187,6 +195,7 @@ function handleFocusOutside(e: Event) {
:modal="modal"
:open="state?.isOpen"
:show-close="closable"
:z-index="zIndex"
close-class="top-3"
@close-auto-focus="handleFocusOutside"
@closed="() => modalApi?.onClosed()"