This commit is contained in:
dap
2024-08-16 07:41:43 +08:00
33 changed files with 935 additions and 481 deletions

View File

@@ -4,7 +4,7 @@ import type { ContentCompactType } from '@vben-core/typings';
import type { CSSProperties } from 'vue';
import { computed } from 'vue';
import { useContentHeightListener } from '@vben-core/composables';
import { useContentStyle } from '@vben-core/composables';
interface Props {
/**
@@ -24,7 +24,7 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {});
const { contentElement } = useContentHeightListener();
const { contentElement, overlayStyle } = useContentStyle();
const style = computed((): CSSProperties => {
const {
@@ -53,7 +53,9 @@ const style = computed((): CSSProperties => {
</script>
<template>
<main ref="contentElement" :style="style" class="bg-background-deep">
<main ref="contentElement" :style="style" class="bg-background-deep relative">
<!-- <BlurShadow :style="shadowStyle" /> -->
<slot :overlay-style="overlayStyle" name="overlay"></slot>
<slot></slot>
</main>
</template>

View File

@@ -519,6 +519,10 @@ function handleOpenMenu() {
class="transition-[margin-top] duration-200"
>
<slot name="content"></slot>
<template #overlay="{ overlayStyle }">
<slot :overlay-style="overlayStyle" name="content-overlay"></slot>
</template>
</LayoutContent>
<LayoutFooter

View File

@@ -72,9 +72,10 @@ function scrollIntoView() {
<template>
<div :style="style" class="tabs-chrome size-full flex-1 overflow-hidden pt-1">
<VbenScrollbar
id="tabs-scrollbar"
class="tabs-chrome__scrollbar h-full"
horizontal
scroll-bar-class="z-10"
scroll-bar-class="z-10 hidden"
>
<!-- footer -> 4px -->
<div

View File

@@ -72,11 +72,12 @@ function scrollIntoView() {
</script>
<template>
<div class="h-full flex-1 overflow-hidden">
<div class="size-full flex-1 overflow-hidden">
<VbenScrollbar
id="tabs-scrollbar"
class="tabs-scrollbar h-full"
horizontal
scroll-bar-class="z-10"
scroll-bar-class="z-10 hidden"
>
<div
:class="contentClass"

View File

@@ -7,8 +7,10 @@ import type { TabsProps } from './types';
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
import { useForwardPropsEmits, useSortable } from '@vben-core/composables';
import { ChevronLeft, ChevronRight } from '@vben-core/icons';
import { Tabs, TabsChrome } from './components';
import { useTabsViewScroll } from './use-tabs-view-scroll';
interface Props extends TabsProps {}
@@ -30,6 +32,8 @@ const emit = defineEmits<{
const forward = useForwardPropsEmits(props, emit);
const { initScrollbar, scrollDirection } = useTabsViewScroll();
const sortableInstance = ref<null | Sortable>(null);
// 可能会找到拖拽的子元素这里需要确保拖拽的dom时tab元素
@@ -104,13 +108,21 @@ async function initTabsSortable() {
sortableInstance.value = await initializeSortable();
}
onMounted(initTabsSortable);
async function init() {
await nextTick();
initTabsSortable();
initScrollbar();
}
onMounted(() => {
init();
});
watch(
() => props.styleType,
() => {
sortableInstance.value?.destroy();
initTabsSortable();
init();
},
);
@@ -120,6 +132,32 @@ onUnmounted(() => {
</script>
<template>
<TabsChrome v-if="styleType === 'chrome'" v-bind="forward" />
<Tabs v-else v-bind="forward" />
<div
:class="{
'overflow-hidden': styleType !== 'chrome',
}"
class="flex h-full flex-1"
>
<!-- 左侧滚动按钮 -->
<span
class="hover:bg-muted text-muted-foreground cursor-pointer border-r px-2"
@click="scrollDirection('left')"
>
<ChevronLeft class="size-4 h-full" />
</span>
<TabsChrome
v-if="styleType === 'chrome'"
v-bind="{ ...forward, ...$attrs, ...$props }"
/>
<Tabs v-else v-bind="{ ...forward, ...$attrs, ...$props }" />
<!-- 左侧滚动按钮 -->
<span
class="hover:bg-muted text-muted-foreground cursor-pointer border-l px-2"
@click="scrollDirection('right')"
>
<ChevronRight class="size-4 h-full" />
</span>
</div>
</template>

View File

@@ -0,0 +1,59 @@
import { nextTick, ref } from 'vue';
type El = Element | null | undefined;
export function useTabsViewScroll(scrollDistance: number = 150) {
const scrollbarEl = ref<El>(null);
const scrollViewportEl = ref<El>(null);
function getScrollClientWidth() {
if (!scrollbarEl.value || !scrollViewportEl.value) return {};
const scrollbarWidth = scrollbarEl.value.clientWidth;
const scrollViewWidth = scrollViewportEl.value.clientWidth;
return {
scrollbarWidth,
scrollViewWidth,
};
}
function scrollDirection(
direction: 'left' | 'right',
distance: number = scrollDistance,
) {
const { scrollbarWidth, scrollViewWidth } = getScrollClientWidth();
if (!scrollbarWidth || !scrollViewWidth) return;
if (scrollbarWidth > scrollViewWidth) return;
scrollViewportEl.value?.scrollBy({
behavior: 'smooth',
left:
direction === 'left'
? -(scrollbarWidth - distance)
: +(scrollbarWidth - distance),
});
}
async function initScrollbar() {
await nextTick();
const barEl = document.querySelector('#tabs-scrollbar');
const viewportEl = barEl?.querySelector(
'div[data-radix-scroll-area-viewport]',
);
scrollbarEl.value = barEl;
scrollViewportEl.value = viewportEl;
const activeItem = viewportEl?.querySelector('.is-active');
activeItem?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
return {
initScrollbar,
scrollDirection,
};
}