Browse Source

refactor(@vben/common-ui): simplify page content sizing

pull/8252/head
Dream 2 weeks ago
parent
commit
fa2b1ec243
  1. 43
      packages/effects/common-ui/src/components/page/__tests__/page.test.ts
  2. 68
      packages/effects/common-ui/src/components/page/page.vue

43
packages/effects/common-ui/src/components/page/__tests__/page.test.ts

@ -59,6 +59,49 @@ describe('page.vue', () => {
expect(contentDiv.classes()).toContain('custom-class');
});
it('uses flex layout for automatic content height', () => {
const wrapper = mount(Page, {
props: {
autoContentHeight: true,
heightOffset: 12,
title: 'Auto height page',
},
});
const content = wrapper.find('[data-layout-region="page-content"]');
const header = wrapper.find('.border-b');
expect(wrapper.classes()).toContain('min-h-0');
expect(wrapper.classes()).toContain('overflow-hidden');
expect(header.classes()).toContain('shrink-0');
expect(content.exists()).toBe(true);
expect(content.classes()).toContain('min-h-0');
expect(content.classes()).toContain('flex-1');
expect(content.classes()).toContain('overflow-y-auto');
expect(content.attributes('style')).toContain(
'--page-content-height-offset: 12px',
);
expect(content.attributes('style')).not.toContain('--vben-content-height');
});
it('positions the footer according to footerFixed', async () => {
const wrapper = mount(Page, {
slots: {
footer: '<p>Footer</p>',
},
});
const footer = wrapper.find('.bg-card');
expect(footer.classes()).toContain('shrink-0');
expect(footer.classes()).not.toContain('absolute');
await wrapper.setProps({ footerFixed: true });
expect(footer.classes()).toContain('absolute');
expect(footer.classes()).toContain('bottom-0');
expect(footer.classes()).not.toContain('shrink-0');
});
it('does not render title slot if title prop is provided', () => {
const wrapper = mount(Page, {
props: {

68
packages/effects/common-ui/src/components/page/page.vue

@ -3,9 +3,8 @@ import type { StyleValue } from 'vue';
import type { PageProps } from './types';
import { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue';
import { computed } from 'vue';
import { CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT } from '@vben-core/shared/constants';
import { cn } from '@vben-core/shared/utils';
defineOptions({
@ -18,45 +17,27 @@ const {
footerFixed = false,
} = defineProps<PageProps>();
const headerHeight = ref(0);
const footerHeight = ref(0);
const shouldAutoHeight = ref(false);
const headerRef = useTemplateRef<HTMLDivElement>('headerRef');
const footerRef = useTemplateRef<HTMLDivElement>('footerRef');
const contentStyle = computed<StyleValue>(() => {
if (autoContentHeight) {
return {
height: `calc(var(${CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT}) - ${headerHeight.value}px - ${footerHeight.value}px - ${typeof heightOffset === 'number' ? `${heightOffset}px` : heightOffset})`,
overflowY: shouldAutoHeight.value ? 'auto' : 'unset',
};
}
return {};
});
async function calcContentHeight() {
if (!autoContentHeight) {
return;
return {};
}
shouldAutoHeight.value = false;
await nextTick();
headerHeight.value = headerRef.value?.offsetHeight || 0;
footerHeight.value = footerFixed ? 0 : footerRef.value?.offsetHeight || 0;
setTimeout(() => {
shouldAutoHeight.value = true;
}, 30);
}
onMounted(() => {
calcContentHeight();
return {
'--page-content-height-offset': `${heightOffset}px`,
marginBlockEnd: 'var(--page-content-height-offset)',
};
});
</script>
<template>
<div class="relative flex h-full flex-col">
<div
:class="
cn(
'relative flex h-full min-h-0 flex-col',
autoContentHeight && 'overflow-hidden',
)
"
>
<div
v-if="
description ||
@ -65,10 +46,9 @@ onMounted(() => {
$slots.title ||
$slots.extra
"
ref="headerRef"
:class="
cn(
'relative flex items-end border-b border-border bg-card px-6 py-4',
'relative flex shrink-0 items-end border-b border-border bg-card px-6 py-4',
headerClass,
)
"
@ -93,15 +73,27 @@ onMounted(() => {
</div>
<div
:class="cn(autoContentHeight ? 'h-full' : 'flex-1', 'p-4', contentClass)"
data-layout-region="page-content"
:class="
cn(
autoContentHeight ? 'min-h-0 flex-1 overflow-y-auto' : 'flex-1',
'p-4',
contentClass,
)
"
:style="contentStyle"
>
<slot></slot>
</div>
<div
v-if="$slots.footer"
ref="footerRef"
:class="cn('align-center flex bg-card px-6 py-4', footerClass)"
:class="
cn(
'align-center flex bg-card px-6 py-4',
footerFixed ? 'absolute inset-x-0 bottom-0' : 'shrink-0',
footerClass,
)
"
>
<slot name="footer"></slot>
</div>

Loading…
Cancel
Save