feat: add resizable and ColPage component (#5188)

* feat: add component resizable

* feat: component `ColPage` with demo
This commit is contained in:
Netfan
2024-12-19 20:37:42 +08:00
committed by GitHub
parent 1853ba1d60
commit acd87b2250
16 changed files with 368 additions and 13 deletions

View File

@@ -0,0 +1,107 @@
<script lang="ts" setup>
import type { ColPageProps } from './types';
import { computed, ref, useSlots } from 'vue';
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from '@vben-core/shadcn-ui';
import Page from '../page/page.vue';
defineOptions({
name: 'ColPage',
inheritAttrs: false,
});
const props = withDefaults(defineProps<ColPageProps>(), {
leftWidth: 30,
rightWidth: 70,
resizable: true,
});
const delegatedProps = computed(() => {
const { leftWidth: _, ...delegated } = props;
return delegated;
});
const slots = useSlots();
const delegatedSlots = computed(() => {
const resultSlots: string[] = [];
for (const key of Object.keys(slots)) {
if (!['default', 'left'].includes(key)) {
resultSlots.push(key);
}
}
return resultSlots;
});
const leftPanelRef = ref<InstanceType<typeof ResizablePanel>>();
function expandLeft() {
leftPanelRef.value?.expand();
}
function collapseLeft() {
leftPanelRef.value?.collapse();
}
defineExpose({
expandLeft,
collapseLeft,
});
</script>
<template>
<Page v-bind="delegatedProps">
<!-- 继承默认的slot -->
<template
v-for="slotName in delegatedSlots"
:key="slotName"
#[slotName]="slotProps"
>
<slot :name="slotName" v-bind="slotProps"></slot>
</template>
<ResizablePanelGroup class="w-full" direction="horizontal">
<ResizablePanel
ref="leftPanelRef"
:collapsed-size="leftCollapsedWidth"
:collapsible="leftCollapsible"
:default-size="leftWidth"
:max-size="leftMaxWidth"
:min-size="leftMinWidth"
>
<template #default="slotProps">
<slot
name="left"
v-bind="{
...slotProps,
expand: expandLeft,
collapse: collapseLeft,
}"
></slot>
</template>
</ResizablePanel>
<ResizableHandle
v-if="resizable"
:style="{ backgroundColor: splitLine ? undefined : 'transparent' }"
:with-handle="splitHandle"
/>
<ResizablePanel
:collapsed-size="rightCollapsedWidth"
:collapsible="rightCollapsible"
:default-size="rightWidth"
:max-size="rightMaxWidth"
:min-size="rightMinWidth"
>
<template #default>
<slot></slot>
</template>
</ResizablePanel>
</ResizablePanelGroup>
</Page>
</template>

View File

@@ -0,0 +1,2 @@
export { default as ColPage } from './col-page.vue';
export * from './types';

View File

@@ -0,0 +1,26 @@
import type { PageProps } from '../page/types';
export interface ColPageProps extends PageProps {
/**
* 左侧宽度
* @default 30
*/
leftWidth?: number;
leftMinWidth?: number;
leftMaxWidth?: number;
leftCollapsedWidth?: number;
leftCollapsible?: boolean;
/**
* 右侧宽度
* @default 70
*/
rightWidth?: number;
rightMinWidth?: number;
rightCollapsedWidth?: number;
rightMaxWidth?: number;
rightCollapsible?: boolean;
resizable?: boolean;
splitLine?: boolean;
splitHandle?: boolean;
}

View File

@@ -1,5 +1,6 @@
export * from './api-component';
export * from './captcha';
export * from './col-page';
export * from './ellipsis-text';
export * from './icon-picker';
export * from './page';

View File

@@ -1 +1,2 @@
export { default as Page } from './page.vue';
export * from './types';

View File

@@ -1,4 +1,6 @@
<script setup lang="ts">
import type { PageProps } from './types';
import {
computed,
nextTick,
@@ -11,23 +13,11 @@ import {
import { CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT } from '@vben-core/shared/constants';
import { cn } from '@vben-core/shared/utils';
interface Props {
title?: string;
description?: string;
contentClass?: string;
/**
* 根据content可见高度自适应
*/
autoContentHeight?: boolean;
headerClass?: string;
footerClass?: string;
}
defineOptions({
name: 'Page',
});
const { autoContentHeight = false } = defineProps<Props>();
const { autoContentHeight = false } = defineProps<PageProps>();
const headerHeight = ref(0);
const footerHeight = ref(0);

View File

@@ -0,0 +1,11 @@
export interface PageProps {
title?: string;
description?: string;
contentClass?: string;
/**
* 根据content可见高度自适应
*/
autoContentHeight?: boolean;
headerClass?: string;
footerClass?: string;
}