Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: modal&drawer support appendToMain and zIndex #5092

Merged
merged 8 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/src/components/common-ui/vben-drawer.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const [Drawer, drawerApi] = useVbenDrawer({

| 属性名 | 描述 | 类型 | 默认值 |
| --- | --- | --- | --- |
| appendToMain | 是否挂载到内容区域(默认挂载到body) | `boolean` | `false` |
| title | 标题 | `string\|slot` | - |
| titleTooltip | 标题提示信息 | `string\|slot` | - |
| description | 描述信息 | `string\|slot` | - |
Expand All @@ -95,6 +96,13 @@ const [Drawer, drawerApi] = useVbenDrawer({
| contentClass | modal内容区域的class | `string` | - |
| footerClass | modal底部区域的class | `string` | - |
| headerClass | modal顶部区域的class | `string` | - |
| zIndex | 抽屉的ZIndex层级 | `number` | `1000` |

::: info appendToMain

`appendToMain`可以指定将抽屉挂载到内容区域,打开抽屉时,内容区域以外的部分(标签栏、导航菜单等等)不会被遮挡。默认情况下,抽屉会挂载到body上。但是:挂载到内容区域时,作为页面根容器的`Page`组件,需要设置`auto-content-height`属性,以便抽屉能够正确计算高度。

:::

### Event

Expand Down
8 changes: 8 additions & 0 deletions docs/src/components/common-ui/vben-modal.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const [Modal, modalApi] = useVbenModal({

| 属性名 | 描述 | 类型 | 默认值 |
| --- | --- | --- | --- |
| appendToMain | 是否挂载到内容区域(默认挂载到body) | `boolean` | `false` |
| title | 标题 | `string\|slot` | - |
| titleTooltip | 标题提示信息 | `string\|slot` | - |
| description | 描述信息 | `string\|slot` | - |
Expand All @@ -106,6 +107,13 @@ const [Modal, modalApi] = useVbenModal({
| footerClass | modal底部区域的class | `string` | - |
| headerClass | modal顶部区域的class | `string` | - |
| bordered | 是否显示border | `boolean` | `false` |
| zIndex | 弹窗的ZIndex层级 | `number` | `1000` |

::: info appendToMain

`appendToMain`可以指定将弹窗挂载到内容区域,打开带这招的弹窗时,内容区域以外的部分(标签栏、导航菜单等等)不会被遮挡。默认情况下,弹窗会挂载到body上。但是:挂载到内容区域时,作为页面根容器的`Page`组件,需要设置`auto-content-height`属性,以便弹窗能够正确计算高度。

:::

### Event

Expand Down
3 changes: 3 additions & 0 deletions packages/@core/base/shared/src/constants/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export const CSS_VARIABLE_LAYOUT_HEADER_HEIGHT = `--vben-header-height`;
/** layout footer 组件的高度 */
export const CSS_VARIABLE_LAYOUT_FOOTER_HEIGHT = `--vben-footer-height`;

/** 内容区域的组件ID */
export const ELEMENT_ID_MAIN_CONTENT = `__vben_main_content`;

/**
* @zh_CN 默认命名空间
*/
Expand Down
1 change: 1 addition & 0 deletions packages/@core/ui-kit/layout-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@vben-core/composables": "workspace:*",
"@vben-core/icons": "workspace:*",
"@vben-core/shadcn-ui": "workspace:*",
"@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vueuse/core": "catalog:",
"vue": "catalog:"
Expand Down
4 changes: 4 additions & 0 deletions packages/@core/ui-kit/layout-ui/src/vben-layout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '@vben-core/composables';
import { Menu } from '@vben-core/icons';
import { VbenIconButton } from '@vben-core/shadcn-ui';
import { ELEMENT_ID_MAIN_CONTENT } from '@vben-core/shared/constants';

import { useMouse, useScroll, useThrottleFn } from '@vueuse/core';

Expand Down Expand Up @@ -457,6 +458,8 @@ function handleHeaderToggle() {
emit('toggleSidebar');
}
}

const idMainContent = ELEMENT_ID_MAIN_CONTENT;
</script>

<template>
Expand Down Expand Up @@ -553,6 +556,7 @@ function handleHeaderToggle() {

<!-- </div> -->
<LayoutContent
:id="idMainContent"
:content-compact="contentCompact"
:content-compact-width="contentCompactWidth"
:padding="contentPadding"
Expand Down
13 changes: 11 additions & 2 deletions packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import type { Component, Ref } from 'vue';
export type DrawerPlacement = 'bottom' | 'left' | 'right' | 'top';

export interface DrawerProps {
/**
* 是否挂载到内容区域
* @default false
*/
appendToMain?: boolean;
/**
* 取消按钮文字
*/
Expand Down Expand Up @@ -59,12 +64,12 @@ export interface DrawerProps {
* 弹窗头部样式
*/
headerClass?: ClassType;

/**
* 弹窗是否显示
* @default false
*/
loading?: boolean;

/**
* 是否显示遮罩
* @default true
Expand All @@ -74,12 +79,12 @@ export interface DrawerProps {
* 是否自动聚焦
*/
openAutoFocus?: boolean;

/**
* 抽屉位置
* @default right
*/
placement?: DrawerPlacement;

/**
* 是否显示取消按钮
* @default true
Expand All @@ -98,6 +103,10 @@ export interface DrawerProps {
* 弹窗标题提示
*/
titleTooltip?: string;
/**
* 抽屉层级
*/
zIndex?: number;
}

export interface DrawerState extends DrawerProps {
Expand Down
13 changes: 12 additions & 1 deletion packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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';

Expand All @@ -31,7 +32,9 @@ interface Props extends DrawerProps {
}

const props = withDefaults(defineProps<Props>(), {
appendToMain: false,
drawerApi: undefined,
zIndex: 1000,
});

const components = globalShareState.getComponents();
Expand All @@ -46,6 +49,7 @@ const { isMobile } = useIsMobile();
const state = props.drawerApi?.useStore?.();

const {
appendToMain,
cancelText,
class: drawerClass,
closable,
Expand All @@ -67,6 +71,7 @@ const {
showConfirmButton,
title,
titleTooltip,
zIndex,
} = usePriorityValues(props, state);

watch(
Expand Down Expand Up @@ -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
Expand All @@ -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',
Expand All @@ -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"
Expand Down
11 changes: 10 additions & 1 deletion packages/@core/ui-kit/popup-ui/src/modal/modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -12,14 +17,14 @@ export interface ModalProps {
* 取消按钮文字
*/
cancelText?: string;

/**
* 是否居中
* @default false
*/
centered?: boolean;

class?: string;

/**
* 是否显示右上角的关闭按钮
* @default true
Expand Down Expand Up @@ -112,6 +117,10 @@ export interface ModalProps {
* 弹窗标题提示
*/
titleTooltip?: string;
/**
* 弹窗层级
*/
zIndex?: number;
}

export interface ModalState extends ModalProps {
Expand Down
9 changes: 9 additions & 0 deletions packages/@core/ui-kit/popup-ui/src/modal/modal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -32,6 +33,7 @@ interface Props extends ModalProps {
}

const props = withDefaults(defineProps<Props>(), {
appendToMain: false,
modalApi: undefined,
});

Expand All @@ -52,6 +54,7 @@ const { isMobile } = useIsMobile();
const state = props.modalApi?.useStore?.();

const {
appendToMain,
bordered,
cancelText,
centered,
Expand All @@ -78,6 +81,7 @@ const {
showConfirmButton,
title,
titleTooltip,
zIndex,
} = usePriorityValues(props, state);

const shouldFullscreen = computed(
Expand Down Expand Up @@ -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
Expand All @@ -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)]',
Expand All @@ -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()"
Expand Down
27 changes: 23 additions & 4 deletions packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ import DialogOverlay from './DialogOverlay.vue';
const props = withDefaults(
defineProps<
{
appendTo?: HTMLElement | string;
class?: ClassType;
closeClass?: ClassType;
modal?: boolean;
open?: boolean;
showClose?: boolean;
zIndex?: number;
} & DialogContentProps
>(),
{ showClose: true },
{ appendTo: 'body', showClose: true, zIndex: 1000 },
);
const emits = defineEmits<
{ close: []; closed: []; opened: [] } & DialogContentEmits
Expand All @@ -45,6 +47,18 @@ const delegatedProps = computed(() => {
return delegated;
});

function isAppendToBody() {
return (
props.appendTo === 'body' ||
props.appendTo === document.body ||
!props.appendTo
);
}

const position = computed(() => {
return isAppendToBody() ? 'fixed' : 'absolute';
});

const forwarded = useForwardPropsEmits(delegatedProps, emits);

const contentRef = ref<InstanceType<typeof DialogContent> | null>(null);
Expand All @@ -64,17 +78,22 @@ defineExpose({
</script>

<template>
<DialogPortal>
<DialogPortal :to="appendTo">
<Transition name="fade">
<DialogOverlay v-if="open && modal" @click="() => emits('close')" />
<DialogOverlay
v-if="open && modal"
:style="{ zIndex, position }"
@click="() => emits('close')"
/>
</Transition>
<DialogContent
ref="contentRef"
:style="{ zIndex, position }"
@animationend="onAnimationEnd"
v-bind="forwarded"
:class="
cn(
'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%] fixed z-[1000] w-full p-6 shadow-lg outline-none sm:rounded-xl',
'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',
props.class,
)
"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,5 @@ useScrollLock();
const id = inject('DISMISSABLE_MODAL_ID');
</script>
<template>
<div
:data-dismissable-modal="id"
class="bg-overlay fixed inset-0 z-[1000]"
></div>
<div :data-dismissable-modal="id" class="bg-overlay fixed inset-0"></div>
mynetfan marked this conversation as resolved.
Show resolved Hide resolved
</template>
Loading
Loading