mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2026-10-06 14:23:19 +08:00
feat(@vben-core/popup-ui): add extra header slot to modal (#8310)
Allow custom content to be rendered in the modal header, positioned to the left of the fullscreen toggle button, via a new `extra` slot. Mirrors the existing `extra` slot in the drawer component. Co-authored-by: EvoX <evox@evomap.ai>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@vben-core/popup-ui': minor
|
||||
---
|
||||
|
||||
feat(@vben-core/popup-ui): support custom content in modal header via extra slot
|
||||
@@ -106,6 +106,43 @@ describe('vben modal', () => {
|
||||
document.dispatchEvent(new MouseEvent('mouseup'));
|
||||
});
|
||||
|
||||
it('renders custom content in the extra header slot', async () => {
|
||||
const mainContent = document.createElement('main');
|
||||
mainContent.id = ELEMENT_ID_MAIN_CONTENT;
|
||||
mainContent.innerHTML = '<div><div></div></div>';
|
||||
document.body.append(mainContent);
|
||||
|
||||
const Consumer = defineComponent(() => {
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
appendToMain: true,
|
||||
fullscreenButton: true,
|
||||
title: 'Slot modal',
|
||||
});
|
||||
onMounted(() => {
|
||||
modalApi.open();
|
||||
});
|
||||
return () =>
|
||||
h(Modal, null, {
|
||||
extra: () => h('span', { class: 'extra-slot-marker' }, 'Custom'),
|
||||
});
|
||||
});
|
||||
const host = document.createElement('div');
|
||||
document.body.append(host);
|
||||
activeApp = createApp(() => h(Consumer));
|
||||
activeApp.mount(host);
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
|
||||
const marker = document.querySelector('.extra-slot-marker');
|
||||
expect(marker).toBeInstanceOf(HTMLElement);
|
||||
if (!(marker instanceof HTMLElement)) return;
|
||||
expect(marker.textContent?.trim()).toBe('Custom');
|
||||
// extra slot 应渲染在 fullscreen 按钮容器内,与全屏按钮同排
|
||||
const container = marker.closest('.absolute');
|
||||
expect(container).toBeInstanceOf(HTMLElement);
|
||||
expect(mainContent.contains(container)).toBe(true);
|
||||
});
|
||||
|
||||
it('fires onClosed via the fallback when no animation event arrives', async () => {
|
||||
vi.useFakeTimers({
|
||||
toFake: [
|
||||
|
||||
@@ -319,14 +319,20 @@ function handleClosed() {
|
||||
<slot></slot>
|
||||
</div>
|
||||
<VbenLoading v-if="showLoading || submitting" spinning />
|
||||
<VbenIconButton
|
||||
v-if="fullscreenButton"
|
||||
class="absolute top-3 right-10 flex-center size-6 rounded-full px-1 text-lg text-foreground/80 opacity-70 transition-opacity hover:bg-accent hover:text-accent-foreground hover:opacity-100 focus:outline-hidden disabled:pointer-events-none"
|
||||
@click="handleFullscreen"
|
||||
<div
|
||||
v-if="fullscreenButton || $slots.extra"
|
||||
class="absolute top-3 right-10 flex items-center gap-1"
|
||||
>
|
||||
<Shrink v-if="fullscreen" class="size-3.5" />
|
||||
<Expand v-else class="size-3.5" />
|
||||
</VbenIconButton>
|
||||
<slot name="extra"></slot>
|
||||
<VbenIconButton
|
||||
v-if="fullscreenButton"
|
||||
class="flex-center size-6 rounded-full px-1 text-lg text-foreground/80 opacity-70 transition-opacity hover:bg-accent hover:text-accent-foreground hover:opacity-100 focus:outline-hidden disabled:pointer-events-none"
|
||||
@click="handleFullscreen"
|
||||
>
|
||||
<Shrink v-if="fullscreen" class="size-3.5" />
|
||||
<Expand v-else class="size-3.5" />
|
||||
</VbenIconButton>
|
||||
</div>
|
||||
|
||||
<DialogFooter
|
||||
ref="footerRef"
|
||||
|
||||
Reference in New Issue
Block a user