perf: optimization of tabbar display (#4169)
* perf: optimization of tabbar display * fix: ci error * chore: typo * chore: typopull/48/MERGE
parent
8987067b5a
commit
0faf7810b6
|
@ -40,11 +40,11 @@
|
||||||
"@vben/styles": "workspace:*",
|
"@vben/styles": "workspace:*",
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"@vben/utils": "workspace:*",
|
"@vben/utils": "workspace:*",
|
||||||
"@vueuse/core": "^10.11.1",
|
"@vueuse/core": "^11.0.0",
|
||||||
"ant-design-vue": "^4.2.3",
|
"ant-design-vue": "^4.2.3",
|
||||||
"dayjs": "^1.11.12",
|
"dayjs": "^1.11.12",
|
||||||
"pinia": "2.2.2",
|
"pinia": "2.2.2",
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.4.37",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "^4.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,11 +40,11 @@
|
||||||
"@vben/styles": "workspace:*",
|
"@vben/styles": "workspace:*",
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"@vben/utils": "workspace:*",
|
"@vben/utils": "workspace:*",
|
||||||
"@vueuse/core": "^10.11.1",
|
"@vueuse/core": "^11.0.0",
|
||||||
"dayjs": "^1.11.12",
|
"dayjs": "^1.11.12",
|
||||||
"element-plus": "^2.8.0",
|
"element-plus": "^2.8.0",
|
||||||
"pinia": "2.2.2",
|
"pinia": "2.2.2",
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.4.37",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "^4.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -40,10 +40,10 @@
|
||||||
"@vben/styles": "workspace:*",
|
"@vben/styles": "workspace:*",
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"@vben/utils": "workspace:*",
|
"@vben/utils": "workspace:*",
|
||||||
"@vueuse/core": "^10.11.1",
|
"@vueuse/core": "^11.0.0",
|
||||||
"naive-ui": "^2.39.0",
|
"naive-ui": "^2.39.0",
|
||||||
"pinia": "2.2.2",
|
"pinia": "2.2.2",
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.4.37",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "^4.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,6 @@
|
||||||
"@nolebase/vitepress-plugin-git-changelog": "^2.4.0",
|
"@nolebase/vitepress-plugin-git-changelog": "^2.4.0",
|
||||||
"@vite-pwa/vitepress": "^0.5.0",
|
"@vite-pwa/vitepress": "^0.5.0",
|
||||||
"vitepress": "^1.3.2",
|
"vitepress": "^1.3.2",
|
||||||
"vue": "^3.4.38"
|
"vue": "^3.4.37"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,8 @@ export async function typescript(): Promise<Linter.Config[]> {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
// '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
||||||
|
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
'@typescript-eslint/no-empty-function': [
|
'@typescript-eslint/no-empty-function': [
|
||||||
|
|
|
@ -94,7 +94,7 @@
|
||||||
"node": ">=20",
|
"node": ">=20",
|
||||||
"pnpm": ">=9"
|
"pnpm": ">=9"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.7.0",
|
"packageManager": "pnpm@9.7.1",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"peerDependencyRules": {
|
"peerDependencyRules": {
|
||||||
"allowedVersions": {
|
"allowedVersions": {
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/vue": "^4.1.2",
|
"@iconify/vue": "^4.1.2",
|
||||||
"lucide-vue-next": "^0.427.0",
|
"lucide-vue-next": "^0.428.0",
|
||||||
"vue": "^3.4.38"
|
"vue": "^3.4.37"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ctrl/tinycolor": "^4.1.0",
|
"@ctrl/tinycolor": "^4.1.0",
|
||||||
"@vue/shared": "^3.4.38",
|
"@vue/shared": "^3.4.37",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.4.37",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "^4.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,20 +107,23 @@ type MergeAll<
|
||||||
? MergeAll<Rest, Merge<R, F>>
|
? MergeAll<Rest, Merge<R, F>>
|
||||||
: R;
|
: R;
|
||||||
|
|
||||||
export {
|
type EmitType = (name: Name, ...args: any[]) => void;
|
||||||
type AnyFunction,
|
|
||||||
type AnyNormalFunction,
|
export type {
|
||||||
type AnyPromiseFunction,
|
AnyFunction,
|
||||||
type DeepPartial,
|
AnyNormalFunction,
|
||||||
type DeepReadonly,
|
AnyPromiseFunction,
|
||||||
type IntervalHandle,
|
DeepPartial,
|
||||||
type MaybeComputedRef,
|
DeepReadonly,
|
||||||
type MaybeReadonlyRef,
|
EmitType,
|
||||||
type Merge,
|
IntervalHandle,
|
||||||
type MergeAll,
|
MaybeComputedRef,
|
||||||
type NonNullable,
|
MaybeReadonlyRef,
|
||||||
type Nullable,
|
Merge,
|
||||||
type ReadonlyRecordable,
|
MergeAll,
|
||||||
type Recordable,
|
NonNullable,
|
||||||
type TimeoutHandle,
|
Nullable,
|
||||||
|
ReadonlyRecordable,
|
||||||
|
Recordable,
|
||||||
|
TimeoutHandle,
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,10 +36,10 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vben-core/shared": "workspace:*",
|
"@vben-core/shared": "workspace:*",
|
||||||
"@vueuse/core": "^10.11.1",
|
"@vueuse/core": "^11.0.0",
|
||||||
"radix-vue": "^1.9.4",
|
"radix-vue": "^1.9.4",
|
||||||
"sortablejs": "^1.15.2",
|
"sortablejs": "^1.15.2",
|
||||||
"vue": "^3.4.38"
|
"vue": "^3.4.37"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/sortablejs": "^1.15.8"
|
"@types/sortablejs": "^1.15.8"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { CSSProperties } from 'vue';
|
import type { CSSProperties } from 'vue';
|
||||||
import { computed, nextTick, onMounted, ref } from 'vue';
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT,
|
CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT,
|
||||||
|
@ -14,6 +14,7 @@ import { useCssVar, useDebounceFn } from '@vueuse/core';
|
||||||
* @zh_CN content style
|
* @zh_CN content style
|
||||||
*/
|
*/
|
||||||
function useContentStyle() {
|
function useContentStyle() {
|
||||||
|
let resizeObserver: null | ResizeObserver = null;
|
||||||
const contentElement = ref<HTMLDivElement | null>(null);
|
const contentElement = ref<HTMLDivElement | null>(null);
|
||||||
const visibleDomRect = ref<null | VisibleDomRect>(null);
|
const visibleDomRect = ref<null | VisibleDomRect>(null);
|
||||||
const contentHeight = useCssVar(CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT);
|
const contentHeight = useCssVar(CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT);
|
||||||
|
@ -41,12 +42,15 @@ function useContentStyle() {
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
if (contentElement.value && !resizeObserver) {
|
||||||
if (contentElement.value) {
|
resizeObserver = new ResizeObserver(debouncedCalcHeight);
|
||||||
const observer = new ResizeObserver(debouncedCalcHeight);
|
resizeObserver.observe(contentElement.value);
|
||||||
observer.observe(contentElement.value);
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
resizeObserver?.disconnect();
|
||||||
|
resizeObserver = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
return { contentElement, overlayStyle, visibleDomRect };
|
return { contentElement, overlayStyle, visibleDomRect };
|
||||||
|
|
|
@ -39,7 +39,7 @@ describe('useSortable', () => {
|
||||||
expect(Sortable.default.create).toHaveBeenCalledWith(
|
expect(Sortable.default.create).toHaveBeenCalledWith(
|
||||||
mockElement,
|
mockElement,
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
animation: 100,
|
animation: 300,
|
||||||
delay: 400,
|
delay: 400,
|
||||||
delayOnTouchOnly: true,
|
delayOnTouchOnly: true,
|
||||||
...customOptions,
|
...customOptions,
|
||||||
|
|
|
@ -18,7 +18,7 @@ function useSortable<T extends HTMLElement>(
|
||||||
// Sortable?.default?.mount?.(AutoScroll);
|
// Sortable?.default?.mount?.(AutoScroll);
|
||||||
|
|
||||||
const sortable = Sortable?.default?.create?.(sortableContainer, {
|
const sortable = Sortable?.default?.create?.(sortableContainer, {
|
||||||
animation: 100,
|
animation: 300,
|
||||||
delay: 400,
|
delay: 400,
|
||||||
delayOnTouchOnly: true,
|
delayOnTouchOnly: true,
|
||||||
...options,
|
...options,
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vben-core/shared": "workspace:*",
|
"@vben-core/shared": "workspace:*",
|
||||||
"@vben-core/typings": "workspace:*",
|
"@vben-core/typings": "workspace:*",
|
||||||
"@vueuse/core": "^10.11.1",
|
"@vueuse/core": "^11.0.0",
|
||||||
"vue": "^3.4.38"
|
"vue": "^3.4.37"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
"@vben-core/icons": "workspace:*",
|
"@vben-core/icons": "workspace:*",
|
||||||
"@vben-core/shadcn-ui": "workspace:*",
|
"@vben-core/shadcn-ui": "workspace:*",
|
||||||
"@vben-core/typings": "workspace:*",
|
"@vben-core/typings": "workspace:*",
|
||||||
"@vueuse/core": "^10.11.1",
|
"@vueuse/core": "^11.0.0",
|
||||||
"vue": "^3.4.38"
|
"vue": "^3.4.37"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
"@vben-core/shadcn-ui": "workspace:*",
|
"@vben-core/shadcn-ui": "workspace:*",
|
||||||
"@vben-core/shared": "workspace:*",
|
"@vben-core/shared": "workspace:*",
|
||||||
"@vben-core/typings": "workspace:*",
|
"@vben-core/typings": "workspace:*",
|
||||||
"@vueuse/core": "^10.11.1",
|
"@vueuse/core": "^11.0.0",
|
||||||
"vue": "^3.4.38"
|
"vue": "^3.4.37"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,10 +46,10 @@
|
||||||
"@vben-core/icons": "workspace:*",
|
"@vben-core/icons": "workspace:*",
|
||||||
"@vben-core/shared": "workspace:*",
|
"@vben-core/shared": "workspace:*",
|
||||||
"@vben-core/typings": "workspace:*",
|
"@vben-core/typings": "workspace:*",
|
||||||
"@vueuse/core": "^10.11.1",
|
"@vueuse/core": "^11.0.0",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"lucide-vue-next": "^0.427.0",
|
"lucide-vue-next": "^0.428.0",
|
||||||
"radix-vue": "^1.9.4",
|
"radix-vue": "^1.9.4",
|
||||||
"vue": "^3.4.38"
|
"vue": "^3.4.37"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { cn } from '@vben-core/shared';
|
import { cn } from '@vben-core/shared';
|
||||||
|
|
||||||
|
@ -11,6 +11,10 @@ interface Props {
|
||||||
scrollBarClass?: any;
|
scrollBarClass?: any;
|
||||||
shadow?: boolean;
|
shadow?: boolean;
|
||||||
shadowBorder?: boolean;
|
shadowBorder?: boolean;
|
||||||
|
shadowBottom?: boolean;
|
||||||
|
shadowLeft?: boolean;
|
||||||
|
shadowRight?: boolean;
|
||||||
|
shadowTop?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
@ -18,29 +22,66 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
horizontal: false,
|
horizontal: false,
|
||||||
shadow: false,
|
shadow: false,
|
||||||
shadowBorder: false,
|
shadowBorder: false,
|
||||||
|
shadowBottom: true,
|
||||||
|
shadowLeft: false,
|
||||||
|
shadowRight: false,
|
||||||
|
shadowTop: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
scrollAt: [{ bottom: boolean; left: boolean; right: boolean; top: boolean }];
|
||||||
|
}>();
|
||||||
|
|
||||||
const isAtTop = ref(true);
|
const isAtTop = ref(true);
|
||||||
|
const isAtRight = ref(false);
|
||||||
const isAtBottom = ref(false);
|
const isAtBottom = ref(false);
|
||||||
|
const isAtLeft = ref(true);
|
||||||
|
|
||||||
|
const showShadowTop = computed(() => props.shadow && props.shadowTop);
|
||||||
|
const showShadowBottom = computed(() => props.shadow && props.shadowBottom);
|
||||||
|
const showShadowLeft = computed(() => props.shadow && props.shadowLeft);
|
||||||
|
const showShadowRight = computed(() => props.shadow && props.shadowRight);
|
||||||
|
|
||||||
|
const computedShadowClasses = computed(() => ({
|
||||||
|
'shadow-both':
|
||||||
|
!isAtLeft.value &&
|
||||||
|
!isAtRight.value &&
|
||||||
|
showShadowLeft.value &&
|
||||||
|
showShadowRight.value,
|
||||||
|
'shadow-left': !isAtLeft.value && showShadowLeft.value,
|
||||||
|
'shadow-right': !isAtRight.value && showShadowRight.value,
|
||||||
|
}));
|
||||||
|
|
||||||
function handleScroll(event: Event) {
|
function handleScroll(event: Event) {
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
const scrollTop = target?.scrollTop ?? 0;
|
const scrollTop = target?.scrollTop ?? 0;
|
||||||
|
const scrollLeft = target?.scrollLeft ?? 0;
|
||||||
const offsetHeight = target?.offsetHeight ?? 0;
|
const offsetHeight = target?.offsetHeight ?? 0;
|
||||||
|
const offsetWidth = target?.offsetWidth ?? 0;
|
||||||
const scrollHeight = target?.scrollHeight ?? 0;
|
const scrollHeight = target?.scrollHeight ?? 0;
|
||||||
|
const scrollWidth = target?.scrollWidth ?? 0;
|
||||||
isAtTop.value = scrollTop <= 0;
|
isAtTop.value = scrollTop <= 0;
|
||||||
|
isAtLeft.value = scrollLeft <= 0;
|
||||||
isAtBottom.value = scrollTop + offsetHeight >= scrollHeight;
|
isAtBottom.value = scrollTop + offsetHeight >= scrollHeight;
|
||||||
|
isAtRight.value = scrollLeft + offsetWidth >= scrollWidth;
|
||||||
|
|
||||||
|
emit('scrollAt', {
|
||||||
|
bottom: isAtBottom.value,
|
||||||
|
left: isAtLeft.value,
|
||||||
|
right: isAtRight.value,
|
||||||
|
top: isAtTop.value,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ScrollArea
|
<ScrollArea
|
||||||
:class="[cn(props.class)]"
|
:class="[cn(props.class), computedShadowClasses]"
|
||||||
:on-scroll="handleScroll"
|
:on-scroll="handleScroll"
|
||||||
class="relative"
|
class="vben-scrollbar relative"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="shadow"
|
v-if="showShadowTop"
|
||||||
:class="{
|
:class="{
|
||||||
'opacity-100': !isAtTop,
|
'opacity-100': !isAtTop,
|
||||||
'border-border border-t': shadowBorder && !isAtTop,
|
'border-border border-t': shadowBorder && !isAtTop,
|
||||||
|
@ -49,7 +90,7 @@ function handleScroll(event: Event) {
|
||||||
></div>
|
></div>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<div
|
<div
|
||||||
v-if="shadow"
|
v-if="showShadowBottom"
|
||||||
:class="{
|
:class="{
|
||||||
'opacity-100': !isAtTop && !isAtBottom,
|
'opacity-100': !isAtTop && !isAtBottom,
|
||||||
'border-border border-b': shadowBorder && !isAtTop && !isAtBottom,
|
'border-border border-b': shadowBorder && !isAtTop && !isAtBottom,
|
||||||
|
@ -65,6 +106,31 @@ function handleScroll(event: Event) {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.vben-scrollbar {
|
||||||
|
&:not(.shadow-both).shadow-left {
|
||||||
|
mask-image: linear-gradient(90deg, transparent, #000 16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.shadow-both).shadow-right {
|
||||||
|
mask-image: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
#000 0%,
|
||||||
|
#000 calc(100% - 16px),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.shadow-both {
|
||||||
|
mask-image: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
transparent,
|
||||||
|
#000 16px,
|
||||||
|
#000 calc(100% - 16px),
|
||||||
|
transparent 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.scrollbar-top-shadow {
|
.scrollbar-top-shadow {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
to bottom,
|
to bottom,
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
"@vben-core/icons": "workspace:*",
|
"@vben-core/icons": "workspace:*",
|
||||||
"@vben-core/shadcn-ui": "workspace:*",
|
"@vben-core/shadcn-ui": "workspace:*",
|
||||||
"@vben-core/typings": "workspace:*",
|
"@vben-core/typings": "workspace:*",
|
||||||
"vue": "^3.4.38"
|
"@vueuse/core": "^11.0.0",
|
||||||
|
"vue": "^3.4.37"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,10 @@ import type { TabDefinition } from '@vben-core/typings';
|
||||||
|
|
||||||
import type { TabConfig, TabsProps } from '../../types';
|
import type { TabConfig, TabsProps } from '../../types';
|
||||||
|
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { MdiPin, X } from '@vben-core/icons';
|
import { MdiPin, X } from '@vben-core/icons';
|
||||||
import { VbenContextMenu, VbenIcon, VbenScrollbar } from '@vben-core/shadcn-ui';
|
import { VbenContextMenu, VbenIcon } from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
interface Props extends TabsProps {}
|
interface Props extends TabsProps {}
|
||||||
|
|
||||||
|
@ -20,17 +20,17 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
contentClass: 'vben-tabs-content',
|
contentClass: 'vben-tabs-content',
|
||||||
contextMenus: () => [],
|
contextMenus: () => [],
|
||||||
gap: 7,
|
gap: 7,
|
||||||
maxWidth: 150,
|
|
||||||
minWidth: 80,
|
|
||||||
tabs: () => [],
|
tabs: () => [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{ close: [string]; unpin: [TabDefinition] }>();
|
const emit = defineEmits<{
|
||||||
|
close: [string];
|
||||||
|
unpin: [TabDefinition];
|
||||||
|
}>();
|
||||||
const active = defineModel<string>('active');
|
const active = defineModel<string>('active');
|
||||||
|
|
||||||
const contentRef = ref();
|
const contentRef = ref();
|
||||||
const tabRef = ref();
|
const tabRef = ref();
|
||||||
const tabWidth = ref<number>(props.maxWidth);
|
|
||||||
|
|
||||||
const style = computed(() => {
|
const style = computed(() => {
|
||||||
const { gap } = props;
|
const { gap } = props;
|
||||||
|
@ -53,148 +53,118 @@ const tabsView = computed((): TabConfig[] => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(active, () => {
|
|
||||||
scrollIntoView();
|
|
||||||
});
|
|
||||||
|
|
||||||
function scrollIntoView() {
|
|
||||||
setTimeout(() => {
|
|
||||||
const element = document.querySelector(`.tabs-chrome__item.is-active`);
|
|
||||||
|
|
||||||
if (element) {
|
|
||||||
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :style="style" class="tabs-chrome size-full flex-1 overflow-hidden pt-1">
|
<div
|
||||||
<VbenScrollbar
|
ref="contentRef"
|
||||||
id="tabs-scrollbar"
|
:class="contentClass"
|
||||||
class="tabs-chrome__scrollbar h-full"
|
:style="style"
|
||||||
horizontal
|
class="tabs-chrome !flex h-full w-max pr-6"
|
||||||
scroll-bar-class="z-10 hidden"
|
>
|
||||||
>
|
<TransitionGroup name="slide-left">
|
||||||
<!-- footer -> 4px -->
|
|
||||||
<div
|
<div
|
||||||
ref="contentRef"
|
v-for="(tab, i) in tabsView"
|
||||||
:class="contentClass"
|
:key="tab.key"
|
||||||
class="relative !flex h-full w-max"
|
ref="tabRef"
|
||||||
|
:class="[{ 'is-active': tab.key === active, dragable: !tab.affixTab }]"
|
||||||
|
:data-active-tab="active"
|
||||||
|
:data-index="i"
|
||||||
|
class="tabs-chrome__item draggable group relative -mr-3 flex h-full select-none items-center"
|
||||||
|
data-tab-item="true"
|
||||||
|
@click="active = tab.key"
|
||||||
>
|
>
|
||||||
<TransitionGroup name="slide-left">
|
<VbenContextMenu
|
||||||
<div
|
:handler-data="tab"
|
||||||
v-for="(tab, i) in tabsView"
|
:menus="contextMenus"
|
||||||
:key="tab.key"
|
:modal="false"
|
||||||
ref="tabRef"
|
item-class="pr-6"
|
||||||
:class="[
|
>
|
||||||
{ 'is-active': tab.key === active, dragable: !tab.affixTab },
|
<div class="relative size-full px-1">
|
||||||
]"
|
<!-- divider -->
|
||||||
:data-active-tab="active"
|
<div
|
||||||
:data-index="i"
|
v-if="i !== 0 && tab.key !== active"
|
||||||
:style="{
|
class="tabs-chrome__divider bg-foreground/50 absolute left-[var(--gap)] top-1/2 z-0 h-4 w-[1px] translate-y-[-50%] transition-all"
|
||||||
width: `${tabWidth}px`,
|
></div>
|
||||||
left: `${(tabWidth - gap * 2) * i}px`,
|
<!-- background -->
|
||||||
}"
|
<div
|
||||||
class="tabs-chrome__item group absolute flex h-full select-none items-center transition-all"
|
class="tabs-chrome__background absolute z-[-1] size-full px-[calc(var(--gap)-1px)] py-0 transition-opacity duration-150"
|
||||||
@click="active = tab.key"
|
|
||||||
>
|
|
||||||
<VbenContextMenu
|
|
||||||
:handler-data="tab"
|
|
||||||
:menus="contextMenus"
|
|
||||||
:modal="false"
|
|
||||||
item-class="pr-6"
|
|
||||||
>
|
>
|
||||||
<div class="size-full">
|
<div
|
||||||
<!-- divider -->
|
class="tabs-chrome__background-content group-[.is-active]:bg-heavy dark:group-[.is-active]:bg-accent h-full rounded-tl-[var(--gap)] rounded-tr-[var(--gap)] duration-150"
|
||||||
<div
|
></div>
|
||||||
v-if="i !== 0 && tab.key !== active"
|
<svg
|
||||||
class="tabs-chrome__divider bg-foreground/60 absolute left-[var(--gap)] top-1/2 z-0 h-4 w-[1px] translate-y-[-50%] transition-all"
|
class="tabs-chrome__background-before group-[.is-active]:fill-primary/15 dark:group-[.is-active]:fill-accent absolute bottom-0 left-[-1px] fill-transparent transition-all duration-150"
|
||||||
></div>
|
height="7"
|
||||||
<!-- background -->
|
width="7"
|
||||||
<div
|
>
|
||||||
class="tabs-chrome__background absolute z-[1] size-full px-[calc(var(--gap)-1px)] py-0 transition-opacity duration-150"
|
<path d="M 0 7 A 7 7 0 0 0 7 0 L 7 7 Z" />
|
||||||
>
|
</svg>
|
||||||
<div
|
<svg
|
||||||
class="tabs-chrome__background-content group-[.is-active]:bg-primary/15 dark:group-[.is-active]:bg-accent h-full rounded-tl-[var(--gap)] rounded-tr-[var(--gap)] duration-150"
|
class="tabs-chrome__background-after group-[.is-active]:fill-primary/15 dark:group-[.is-active]:fill-accent absolute bottom-0 right-[-1px] fill-transparent transition-all duration-150"
|
||||||
></div>
|
height="7"
|
||||||
<svg
|
width="7"
|
||||||
class="tabs-chrome__background-before group-[.is-active]:fill-primary/15 dark:group-[.is-active]:fill-accent absolute bottom-0 left-[-1px] fill-transparent transition-all duration-150"
|
>
|
||||||
height="7"
|
<path d="M 0 0 A 7 7 0 0 0 7 7 L 0 7 Z" />
|
||||||
width="7"
|
</svg>
|
||||||
>
|
</div>
|
||||||
<path d="M 0 7 A 7 7 0 0 0 7 0 L 7 7 Z" />
|
|
||||||
</svg>
|
|
||||||
<svg
|
|
||||||
class="tabs-chrome__background-after group-[.is-active]:fill-primary/15 dark:group-[.is-active]:fill-accent absolute bottom-0 right-[-1px] fill-transparent transition-all duration-150"
|
|
||||||
height="7"
|
|
||||||
width="7"
|
|
||||||
>
|
|
||||||
<path d="M 0 0 A 7 7 0 0 0 7 7 L 0 7 Z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- extra -->
|
<!-- extra -->
|
||||||
<div
|
<div
|
||||||
class="tabs-chrome__extra absolute right-[calc(var(--gap)*1.5)] top-1/2 z-[3] size-4 translate-y-[-50%]"
|
class="tabs-chrome__extra absolute right-[var(--gap)] top-1/2 z-[3] size-4 translate-y-[-50%]"
|
||||||
>
|
>
|
||||||
<!-- close-icon -->
|
<!-- close-icon -->
|
||||||
<X
|
<X
|
||||||
v-show="
|
v-show="!tab.affixTab && tabsView.length > 1 && tab.closable"
|
||||||
!tab.affixTab && tabsView.length > 1 && tab.closable
|
class="hover:bg-accent stroke-accent-foreground/80 hover:stroke-accent-foreground text-accent-foreground/80 group-[.is-active]:text-accent-foreground mt-[2px] size-3 cursor-pointer rounded-full transition-all"
|
||||||
"
|
@click.stop="() => emit('close', tab.key)"
|
||||||
class="hover:bg-accent stroke-accent-foreground/80 hover:stroke-accent-foreground dark:group-[.is-active]:text-accent-foreground group-[.is-active]:text-primary mt-[2px] size-3 cursor-pointer rounded-full transition-all"
|
/>
|
||||||
@click.stop="() => emit('close', tab.key)"
|
<MdiPin
|
||||||
/>
|
v-show="tab.affixTab && tabsView.length > 1 && tab.closable"
|
||||||
<MdiPin
|
class="hover:text-accent-foreground text-accent-foreground/80 group-[.is-active]:text-accent-foreground mt-[2px] size-3.5 cursor-pointer rounded-full transition-all"
|
||||||
v-show="tab.affixTab && tabsView.length > 1 && tab.closable"
|
@click.stop="() => emit('unpin', tab)"
|
||||||
class="hover:bg-accent hover:stroke-accent-foreground group-[.is-active]:text-primary dark:group-[.is-active]:text-accent-foreground mt-[2px] size-3.5 cursor-pointer rounded-full transition-all"
|
/>
|
||||||
@click.stop="() => emit('unpin', tab)"
|
</div>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- tab-item-main -->
|
<!-- tab-item-main -->
|
||||||
<div
|
<div
|
||||||
class="tabs-chrome__item-main group-[.is-active]:text-primary dark:group-[.is-active]:text-accent-foreground text-accent-foreground absolute left-0 right-0 z-[2] mx-[calc(var(--gap)*2)] my-0 flex h-full items-center overflow-hidden rounded-tl-[5px] rounded-tr-[5px] pr-4 duration-150 group-hover:pr-3"
|
class="tabs-chrome__item-main group-[.is-active]:text-accent-foreground dark:group-[.is-active]:text-accent-foreground text-accent-foreground z-[2] mx-[calc(var(--gap)*2)] my-0 flex h-full items-center overflow-hidden rounded-tl-[5px] rounded-tr-[5px] pl-2 pr-4 duration-150"
|
||||||
>
|
>
|
||||||
<VbenIcon
|
<VbenIcon
|
||||||
v-if="showIcon"
|
v-if="showIcon"
|
||||||
:icon="tab.icon"
|
:icon="tab.icon"
|
||||||
class="ml-[var(--gap)] flex size-4 items-center overflow-hidden"
|
class="mr-1 flex size-4 items-center overflow-hidden"
|
||||||
fallback
|
fallback
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span
|
<span class="flex-1 overflow-hidden whitespace-nowrap text-sm">
|
||||||
class="tabs-chrome__label ml-[var(--gap)] flex-1 overflow-hidden whitespace-nowrap text-sm"
|
{{ tab.title }}
|
||||||
>
|
</span>
|
||||||
{{ tab.title }}
|
</div>
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</VbenContextMenu>
|
|
||||||
</div>
|
</div>
|
||||||
</TransitionGroup>
|
</VbenContextMenu>
|
||||||
</div>
|
</div>
|
||||||
<!-- footer -->
|
</TransitionGroup>
|
||||||
<!-- <div class="bg-background h-1"></div> -->
|
|
||||||
</VbenScrollbar>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.tabs-chrome {
|
.tabs-chrome {
|
||||||
.dragging {
|
/* .dragging { */
|
||||||
.tabs-chrome__item-main {
|
|
||||||
|
/* .tabs-chrome__item-main {
|
||||||
@apply pr-0;
|
@apply pr-0;
|
||||||
}
|
} */
|
||||||
|
|
||||||
.tabs-chrome__extra {
|
/* .tabs-chrome__extra {
|
||||||
@apply hidden;
|
@apply hidden;
|
||||||
}
|
} */
|
||||||
}
|
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
&__item:not(.dragging) {
|
||||||
|
@apply cursor-pointer;
|
||||||
|
|
||||||
&__item {
|
|
||||||
&:hover:not(.is-active) {
|
&:hover:not(.is-active) {
|
||||||
& + .tabs-chrome__item {
|
& + .tabs-chrome__item {
|
||||||
.tabs-chrome__divider {
|
.tabs-chrome__divider {
|
||||||
|
@ -207,13 +177,10 @@ function scrollIntoView() {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-chrome__background {
|
.tabs-chrome__background {
|
||||||
&-content {
|
@apply pb-[2px];
|
||||||
@apply bg-accent mx-1 rounded-md pb-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-before,
|
&-content {
|
||||||
&-after {
|
@apply bg-accent-hover mx-[2px] rounded-md;
|
||||||
@apply fill-primary/0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,30 +193,7 @@ function scrollIntoView() {
|
||||||
@apply opacity-0 !important;
|
@apply opacity-0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-chrome__background {
|
|
||||||
@apply opacity-100;
|
|
||||||
|
|
||||||
/* &-content {
|
|
||||||
@apply bg-accent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-before,
|
|
||||||
&-after {
|
|
||||||
@apply fill-heavy;
|
|
||||||
} */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__scrollbar,
|
|
||||||
&__label {
|
|
||||||
mask-image: linear-gradient(
|
|
||||||
90deg,
|
|
||||||
#000 0%,
|
|
||||||
#000 calc(100% - 16px),
|
|
||||||
transparent
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -3,10 +3,10 @@ import type { TabDefinition } from '@vben-core/typings';
|
||||||
|
|
||||||
import type { TabConfig, TabsProps } from '../../types';
|
import type { TabConfig, TabsProps } from '../../types';
|
||||||
|
|
||||||
import { computed, watch } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
import { MdiPin, X } from '@vben-core/icons';
|
import { MdiPin, X } from '@vben-core/icons';
|
||||||
import { VbenContextMenu, VbenIcon, VbenScrollbar } from '@vben-core/shadcn-ui';
|
import { VbenContextMenu, VbenIcon } from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
interface Props extends TabsProps {}
|
interface Props extends TabsProps {}
|
||||||
|
|
||||||
|
@ -21,7 +21,10 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
tabs: () => [],
|
tabs: () => [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{ close: [string]; unpin: [TabDefinition] }>();
|
const emit = defineEmits<{
|
||||||
|
close: [string];
|
||||||
|
unpin: [TabDefinition];
|
||||||
|
}>();
|
||||||
const active = defineModel<string>('active');
|
const active = defineModel<string>('active');
|
||||||
|
|
||||||
const typeWithClass = computed(() => {
|
const typeWithClass = computed(() => {
|
||||||
|
@ -55,108 +58,71 @@ const tabsView = computed((): TabConfig[] => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(active, () => {
|
|
||||||
scrollIntoView();
|
|
||||||
});
|
|
||||||
|
|
||||||
function scrollIntoView() {
|
|
||||||
setTimeout(() => {
|
|
||||||
const element = document.querySelector(`.tabs-chrome__item.is-active`);
|
|
||||||
|
|
||||||
if (element) {
|
|
||||||
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="size-full flex-1 overflow-hidden">
|
<div
|
||||||
<VbenScrollbar
|
:class="contentClass"
|
||||||
id="tabs-scrollbar"
|
class="relative !flex h-full w-max items-center pr-6"
|
||||||
class="tabs-scrollbar h-full"
|
>
|
||||||
horizontal
|
<TransitionGroup name="slide-left">
|
||||||
scroll-bar-class="z-10 hidden"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
:class="contentClass"
|
v-for="(tab, i) in tabsView"
|
||||||
class="relative !flex h-full w-max items-center"
|
:key="tab.key"
|
||||||
|
:class="[
|
||||||
|
{
|
||||||
|
'is-active dark:bg-accent bg-primary/15': tab.key === active,
|
||||||
|
dragable: !tab.affixTab,
|
||||||
|
},
|
||||||
|
typeWithClass.content,
|
||||||
|
]"
|
||||||
|
:data-index="i"
|
||||||
|
class="tab-item [&:not(.is-active)]:hover:bg-accent group relative flex cursor-pointer select-none"
|
||||||
|
data-tab-item="true"
|
||||||
|
@click="active = tab.key"
|
||||||
>
|
>
|
||||||
<TransitionGroup name="slide-left">
|
<VbenContextMenu
|
||||||
<div
|
:handler-data="tab"
|
||||||
v-for="(tab, i) in tabsView"
|
:menus="contextMenus"
|
||||||
:key="tab.key"
|
:modal="false"
|
||||||
:class="[
|
item-class="pr-6"
|
||||||
{
|
>
|
||||||
'is-active dark:bg-accent bg-primary/15': tab.key === active,
|
<div class="relative flex size-full items-center">
|
||||||
dragable: !tab.affixTab,
|
<!-- extra -->
|
||||||
},
|
<div
|
||||||
typeWithClass.content,
|
class="absolute right-1.5 top-1/2 z-[3] translate-y-[-50%] overflow-hidden"
|
||||||
]"
|
|
||||||
:data-index="i"
|
|
||||||
class="tabs-chrome__item [&:not(.is-active)]:hover:bg-accent group relative flex cursor-pointer select-none transition-all duration-300"
|
|
||||||
@click="active = tab.key"
|
|
||||||
>
|
|
||||||
<VbenContextMenu
|
|
||||||
:handler-data="tab"
|
|
||||||
:menus="contextMenus"
|
|
||||||
:modal="false"
|
|
||||||
item-class="pr-6"
|
|
||||||
>
|
>
|
||||||
<div class="relative flex size-full items-center">
|
<!-- close-icon -->
|
||||||
<!-- extra -->
|
<X
|
||||||
<div
|
v-show="!tab.affixTab && tabsView.length > 1 && tab.closable"
|
||||||
class="absolute right-1.5 top-1/2 z-[3] translate-y-[-50%] overflow-hidden"
|
class="hover:bg-accent stroke-accent-foreground/80 hover:stroke-accent-foreground dark:group-[.is-active]:text-accent-foreground group-[.is-active]:text-primary size-3 cursor-pointer rounded-full transition-all"
|
||||||
>
|
@click.stop="() => emit('close', tab.key)"
|
||||||
<!-- close-icon -->
|
/>
|
||||||
<X
|
<MdiPin
|
||||||
v-show="
|
v-show="tab.affixTab && tabsView.length > 1 && tab.closable"
|
||||||
!tab.affixTab && tabsView.length > 1 && tab.closable
|
class="hover:bg-accent hover:stroke-accent-foreground group-[.is-active]:text-primary dark:group-[.is-active]:text-accent-foreground mt-[2px] size-3.5 cursor-pointer rounded-full transition-all"
|
||||||
"
|
@click.stop="() => emit('unpin', tab)"
|
||||||
class="hover:bg-accent stroke-accent-foreground/80 hover:stroke-accent-foreground dark:group-[.is-active]:text-accent-foreground group-[.is-active]:text-primary size-3 cursor-pointer rounded-full transition-all"
|
/>
|
||||||
@click.stop="() => emit('close', tab.key)"
|
</div>
|
||||||
/>
|
|
||||||
<MdiPin
|
|
||||||
v-show="tab.affixTab && tabsView.length > 1 && tab.closable"
|
|
||||||
class="hover:bg-accent hover:stroke-accent-foreground group-[.is-active]:text-primary dark:group-[.is-active]:text-accent-foreground mt-[2px] size-3.5 cursor-pointer rounded-full transition-all"
|
|
||||||
@click.stop="() => emit('unpin', tab)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- tab-item-main -->
|
<!-- tab-item-main -->
|
||||||
<div
|
<div
|
||||||
class="text-accent-foreground group-[.is-active]:text-primary dark:group-[.is-active]:text-accent-foreground mx-3 mr-4 flex h-full items-center overflow-hidden rounded-tl-[5px] rounded-tr-[5px] pr-3 transition-all duration-300"
|
class="text-accent-foreground group-[.is-active]:text-primary dark:group-[.is-active]:text-accent-foreground mx-3 mr-4 flex h-full items-center overflow-hidden rounded-tl-[5px] rounded-tr-[5px] pr-3 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<VbenIcon
|
<VbenIcon
|
||||||
v-if="showIcon"
|
v-if="showIcon"
|
||||||
:icon="tab.icon"
|
:icon="tab.icon"
|
||||||
class="mr-2 flex size-4 items-center overflow-hidden"
|
class="mr-2 flex size-4 items-center overflow-hidden"
|
||||||
fallback
|
fallback
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span
|
<span class="flex-1 overflow-hidden whitespace-nowrap text-sm">
|
||||||
class="flex-1 overflow-hidden whitespace-nowrap text-sm"
|
{{ tab.title }}
|
||||||
>
|
</span>
|
||||||
{{ tab.title }}
|
</div>
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</VbenContextMenu>
|
|
||||||
</div>
|
</div>
|
||||||
</TransitionGroup>
|
</VbenContextMenu>
|
||||||
</div>
|
</div>
|
||||||
</VbenScrollbar>
|
</TransitionGroup>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.tabs-scrollbar {
|
|
||||||
mask-image: linear-gradient(
|
|
||||||
90deg,
|
|
||||||
#000 0%,
|
|
||||||
#000 calc(100% - 16px),
|
|
||||||
transparent
|
|
||||||
);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Sortable } from '@vben-core/composables';
|
import type { TabsEmits, TabsProps } from './types';
|
||||||
import type { TabDefinition } from '@vben-core/typings';
|
|
||||||
|
|
||||||
import type { TabsProps } from './types';
|
import { useForwardPropsEmits } from '@vben-core/composables';
|
||||||
|
|
||||||
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
||||||
|
|
||||||
import { useForwardPropsEmits, useSortable } from '@vben-core/composables';
|
|
||||||
import { ChevronLeft, ChevronRight } from '@vben-core/icons';
|
import { ChevronLeft, ChevronRight } from '@vben-core/icons';
|
||||||
|
import { VbenScrollbar } from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
import { Tabs, TabsChrome } from './components';
|
import { Tabs, TabsChrome } from './components';
|
||||||
|
import { useTabsDrag } from './use-tabs-drag';
|
||||||
import { useTabsViewScroll } from './use-tabs-view-scroll';
|
import { useTabsViewScroll } from './use-tabs-view-scroll';
|
||||||
|
|
||||||
interface Props extends TabsProps {}
|
interface Props extends TabsProps {}
|
||||||
|
@ -24,136 +21,69 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
styleType: 'chrome',
|
styleType: 'chrome',
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<TabsEmits>();
|
||||||
close: [string];
|
|
||||||
sortTabs: [number, number];
|
|
||||||
unpin: [TabDefinition];
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const forward = useForwardPropsEmits(props, emit);
|
const forward = useForwardPropsEmits(props, emit);
|
||||||
|
|
||||||
const { initScrollbar, scrollDirection } = useTabsViewScroll();
|
const {
|
||||||
|
handleScrollAt,
|
||||||
|
scrollbarRef,
|
||||||
|
scrollDirection,
|
||||||
|
scrollIsAtLeft,
|
||||||
|
scrollIsAtRight,
|
||||||
|
showScrollButton,
|
||||||
|
} = useTabsViewScroll(props);
|
||||||
|
|
||||||
const sortableInstance = ref<null | Sortable>(null);
|
useTabsDrag(props, emit);
|
||||||
|
|
||||||
// 可能会找到拖拽的子元素,这里需要确保拖拽的dom时tab元素
|
|
||||||
function findParentElement(element: HTMLElement) {
|
|
||||||
const parentCls = 'group';
|
|
||||||
return element.classList.contains(parentCls)
|
|
||||||
? element
|
|
||||||
: element.closest(`.${parentCls}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initTabsSortable() {
|
|
||||||
await nextTick();
|
|
||||||
const { contentClass } = props;
|
|
||||||
|
|
||||||
const el = document.querySelectorAll(`.${contentClass}`)?.[0] as HTMLElement;
|
|
||||||
|
|
||||||
const resetElState = () => {
|
|
||||||
el.style.cursor = 'default';
|
|
||||||
el.classList.remove('dragging');
|
|
||||||
};
|
|
||||||
|
|
||||||
const { initializeSortable } = useSortable(el, {
|
|
||||||
filter: (_evt, target: HTMLElement) => {
|
|
||||||
const parent = findParentElement(target);
|
|
||||||
const dragable = parent?.classList.contains('dragable');
|
|
||||||
return !dragable || !props.dragable;
|
|
||||||
},
|
|
||||||
onEnd(evt) {
|
|
||||||
const { newIndex, oldIndex } = evt;
|
|
||||||
// const fromElement = evt.item;
|
|
||||||
const { srcElement } = (evt as any).originalEvent;
|
|
||||||
|
|
||||||
if (!srcElement) {
|
|
||||||
resetElState();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const srcParent = findParentElement(srcElement);
|
|
||||||
|
|
||||||
if (!srcParent) {
|
|
||||||
resetElState();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!srcParent.classList.contains('dragable')) {
|
|
||||||
resetElState();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
oldIndex !== undefined &&
|
|
||||||
newIndex !== undefined &&
|
|
||||||
!Number.isNaN(oldIndex) &&
|
|
||||||
!Number.isNaN(newIndex) &&
|
|
||||||
oldIndex !== newIndex
|
|
||||||
) {
|
|
||||||
emit('sortTabs', oldIndex, newIndex);
|
|
||||||
}
|
|
||||||
resetElState();
|
|
||||||
},
|
|
||||||
onMove(evt) {
|
|
||||||
const parent = findParentElement(evt.related);
|
|
||||||
return parent?.classList.contains('dragable') && props.dragable;
|
|
||||||
},
|
|
||||||
onStart: () => {
|
|
||||||
el.style.cursor = 'grabbing';
|
|
||||||
el.classList.add('dragging');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
sortableInstance.value = await initializeSortable();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
await nextTick();
|
|
||||||
initTabsSortable();
|
|
||||||
initScrollbar();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
init();
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.styleType,
|
|
||||||
() => {
|
|
||||||
sortableInstance.value?.destroy();
|
|
||||||
init();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
sortableInstance.value?.destroy();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="flex h-full flex-1 overflow-hidden">
|
||||||
:class="{
|
|
||||||
'overflow-hidden': styleType !== 'chrome',
|
|
||||||
}"
|
|
||||||
class="flex h-full flex-1"
|
|
||||||
>
|
|
||||||
<!-- 左侧滚动按钮 -->
|
<!-- 左侧滚动按钮 -->
|
||||||
<span
|
<span
|
||||||
class="hover:bg-muted text-muted-foreground cursor-pointer border-r px-2"
|
v-show="showScrollButton"
|
||||||
|
:class="{
|
||||||
|
'hover:bg-muted text-muted-foreground cursor-pointer': !scrollIsAtLeft,
|
||||||
|
'pointer-events-none opacity-30': scrollIsAtLeft,
|
||||||
|
}"
|
||||||
|
class="border-r px-2"
|
||||||
@click="scrollDirection('left')"
|
@click="scrollDirection('left')"
|
||||||
>
|
>
|
||||||
<ChevronLeft class="size-4 h-full" />
|
<ChevronLeft class="size-4 h-full" />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<TabsChrome
|
<div
|
||||||
v-if="styleType === 'chrome'"
|
:class="{
|
||||||
v-bind="{ ...forward, ...$attrs, ...$props }"
|
'pt-[3px]': styleType === 'chrome',
|
||||||
/>
|
}"
|
||||||
<Tabs v-else v-bind="{ ...forward, ...$attrs, ...$props }" />
|
class="size-full flex-1 overflow-hidden"
|
||||||
|
>
|
||||||
|
<VbenScrollbar
|
||||||
|
ref="scrollbarRef"
|
||||||
|
class="h-full"
|
||||||
|
horizontal
|
||||||
|
scroll-bar-class="z-10 hidden"
|
||||||
|
shadow
|
||||||
|
shadow-left
|
||||||
|
shadow-right
|
||||||
|
@scroll-at="handleScrollAt"
|
||||||
|
>
|
||||||
|
<TabsChrome
|
||||||
|
v-if="styleType === 'chrome'"
|
||||||
|
v-bind="{ ...forward, ...$attrs, ...$props }"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Tabs v-else v-bind="{ ...forward, ...$attrs, ...$props }" />
|
||||||
|
</VbenScrollbar>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 左侧滚动按钮 -->
|
<!-- 左侧滚动按钮 -->
|
||||||
<span
|
<span
|
||||||
|
v-show="showScrollButton"
|
||||||
|
:class="{
|
||||||
|
'hover:bg-muted text-muted-foreground cursor-pointer': !scrollIsAtRight,
|
||||||
|
'pointer-events-none opacity-30': scrollIsAtRight,
|
||||||
|
}"
|
||||||
class="hover:bg-muted text-muted-foreground cursor-pointer border-l px-2"
|
class="hover:bg-muted text-muted-foreground cursor-pointer border-l px-2"
|
||||||
@click="scrollDirection('right')"
|
@click="scrollDirection('right')"
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
import type { IContextMenuItem } from '@vben-core/shadcn-ui';
|
import type { IContextMenuItem } from '@vben-core/shadcn-ui';
|
||||||
import type { TabDefinition, TabsStyleType } from '@vben-core/typings';
|
import type { TabDefinition, TabsStyleType } from '@vben-core/typings';
|
||||||
|
|
||||||
interface TabsProps {
|
export type TabsEmits = {
|
||||||
|
close: [string];
|
||||||
|
sortTabs: [number, number];
|
||||||
|
unpin: [TabDefinition];
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface TabsProps {
|
||||||
|
active?: string;
|
||||||
/**
|
/**
|
||||||
* @zh_CN content class
|
* @zh_CN content class
|
||||||
* @default tabs-chrome
|
* @default tabs-chrome
|
||||||
|
@ -48,12 +55,10 @@ interface TabsProps {
|
||||||
tabs?: TabDefinition[];
|
tabs?: TabDefinition[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TabConfig extends TabDefinition {
|
export interface TabConfig extends TabDefinition {
|
||||||
affixTab: boolean;
|
affixTab: boolean;
|
||||||
closable: boolean;
|
closable: boolean;
|
||||||
icon: string;
|
icon: string;
|
||||||
key: string;
|
key: string;
|
||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type { TabConfig, TabsProps };
|
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
import type { EmitType } from '@vben-core/typings';
|
||||||
|
|
||||||
|
import type { TabsProps } from './types';
|
||||||
|
|
||||||
|
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { type Sortable, useSortable } from '@vben-core/composables';
|
||||||
|
|
||||||
|
// 可能会找到拖拽的子元素,这里需要确保拖拽的dom时tab元素
|
||||||
|
function findParentElement(element: HTMLElement) {
|
||||||
|
const parentCls = 'group';
|
||||||
|
return element.classList.contains(parentCls)
|
||||||
|
? element
|
||||||
|
: element.closest(`.${parentCls}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTabsDrag(props: TabsProps, emit: EmitType) {
|
||||||
|
const sortableInstance = ref<null | Sortable>(null);
|
||||||
|
|
||||||
|
async function initTabsSortable() {
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
const el = document.querySelectorAll(
|
||||||
|
`.${props.contentClass}`,
|
||||||
|
)?.[0] as HTMLElement;
|
||||||
|
|
||||||
|
if (!el) {
|
||||||
|
console.warn('Element not found for sortable initialization');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetElState = async () => {
|
||||||
|
el.style.cursor = 'default';
|
||||||
|
el.classList.remove('dragging');
|
||||||
|
el.querySelector('.draggable')?.classList.remove('dragging');
|
||||||
|
};
|
||||||
|
|
||||||
|
const { initializeSortable } = useSortable(el, {
|
||||||
|
filter: (_evt, target: HTMLElement) => {
|
||||||
|
const parent = findParentElement(target);
|
||||||
|
const dragable = parent?.classList.contains('dragable');
|
||||||
|
return !dragable || !props.dragable;
|
||||||
|
},
|
||||||
|
onEnd(evt) {
|
||||||
|
const { newIndex, oldIndex } = evt;
|
||||||
|
// const fromElement = evt.item;
|
||||||
|
const { srcElement } = (evt as any).originalEvent;
|
||||||
|
|
||||||
|
if (!srcElement) {
|
||||||
|
resetElState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const srcParent = findParentElement(srcElement);
|
||||||
|
|
||||||
|
if (!srcParent) {
|
||||||
|
resetElState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!srcParent.classList.contains('dragable')) {
|
||||||
|
resetElState();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
oldIndex !== undefined &&
|
||||||
|
newIndex !== undefined &&
|
||||||
|
!Number.isNaN(oldIndex) &&
|
||||||
|
!Number.isNaN(newIndex) &&
|
||||||
|
oldIndex !== newIndex
|
||||||
|
) {
|
||||||
|
emit('sortTabs', oldIndex, newIndex);
|
||||||
|
}
|
||||||
|
resetElState();
|
||||||
|
},
|
||||||
|
onMove(evt) {
|
||||||
|
const parent = findParentElement(evt.related);
|
||||||
|
return parent?.classList.contains('dragable') && props.dragable;
|
||||||
|
},
|
||||||
|
onStart: () => {
|
||||||
|
el.style.cursor = 'grabbing';
|
||||||
|
el.querySelector('.draggable')?.classList.add('dragging');
|
||||||
|
// el.classList.add('dragging');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
sortableInstance.value = await initializeSortable();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
await nextTick();
|
||||||
|
initTabsSortable();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(init);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.styleType,
|
||||||
|
() => {
|
||||||
|
sortableInstance.value?.destroy();
|
||||||
|
init();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
sortableInstance.value?.destroy();
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,15 +1,28 @@
|
||||||
import { nextTick, ref } from 'vue';
|
import type { TabsProps } from './types';
|
||||||
|
|
||||||
type El = Element | null | undefined;
|
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
export function useTabsViewScroll(scrollDistance: number = 150) {
|
import { VbenScrollbar } from '@vben-core/shadcn-ui';
|
||||||
const scrollbarEl = ref<El>(null);
|
|
||||||
const scrollViewportEl = ref<El>(null);
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
|
|
||||||
|
type DomElement = Element | null | undefined;
|
||||||
|
|
||||||
|
export function useTabsViewScroll(props: TabsProps) {
|
||||||
|
let resizeObserver: null | ResizeObserver = null;
|
||||||
|
let mutationObserver: MutationObserver | null = null;
|
||||||
|
let tabItemCount = 0;
|
||||||
|
const scrollbarRef = ref<InstanceType<typeof VbenScrollbar> | null>(null);
|
||||||
|
const scrollViewportEl = ref<DomElement>(null);
|
||||||
|
const showScrollButton = ref(false);
|
||||||
|
const scrollIsAtLeft = ref(true);
|
||||||
|
const scrollIsAtRight = ref(false);
|
||||||
|
|
||||||
function getScrollClientWidth() {
|
function getScrollClientWidth() {
|
||||||
if (!scrollbarEl.value || !scrollViewportEl.value) return {};
|
const scrollbarEl = scrollbarRef.value?.$el;
|
||||||
|
if (!scrollbarEl || !scrollViewportEl.value) return {};
|
||||||
|
|
||||||
const scrollbarWidth = scrollbarEl.value.clientWidth;
|
const scrollbarWidth = scrollbarEl.clientWidth;
|
||||||
const scrollViewWidth = scrollViewportEl.value.clientWidth;
|
const scrollViewWidth = scrollViewportEl.value.clientWidth;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -20,7 +33,7 @@ export function useTabsViewScroll(scrollDistance: number = 150) {
|
||||||
|
|
||||||
function scrollDirection(
|
function scrollDirection(
|
||||||
direction: 'left' | 'right',
|
direction: 'left' | 'right',
|
||||||
distance: number = scrollDistance,
|
distance: number = 150,
|
||||||
) {
|
) {
|
||||||
const { scrollbarWidth, scrollViewWidth } = getScrollClientWidth();
|
const { scrollbarWidth, scrollViewWidth } = getScrollClientWidth();
|
||||||
|
|
||||||
|
@ -39,21 +52,142 @@ export function useTabsViewScroll(scrollDistance: number = 150) {
|
||||||
|
|
||||||
async function initScrollbar() {
|
async function initScrollbar() {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
const barEl = document.querySelector('#tabs-scrollbar');
|
|
||||||
|
|
||||||
const viewportEl = barEl?.querySelector(
|
const scrollbarEl = scrollbarRef.value?.$el;
|
||||||
|
if (!scrollbarEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewportEl = scrollbarEl?.querySelector(
|
||||||
'div[data-radix-scroll-area-viewport]',
|
'div[data-radix-scroll-area-viewport]',
|
||||||
);
|
);
|
||||||
|
|
||||||
scrollbarEl.value = barEl;
|
|
||||||
scrollViewportEl.value = viewportEl;
|
scrollViewportEl.value = viewportEl;
|
||||||
|
calcShowScrollbarButton();
|
||||||
|
|
||||||
const activeItem = viewportEl?.querySelector('.is-active');
|
await nextTick();
|
||||||
activeItem?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
scrollToActiveIntoView();
|
||||||
|
|
||||||
|
// 监听大小变化
|
||||||
|
resizeObserver?.disconnect();
|
||||||
|
resizeObserver = new ResizeObserver(
|
||||||
|
useDebounceFn((_entries: ResizeObserverEntry[]) => {
|
||||||
|
calcShowScrollbarButton();
|
||||||
|
}, 100),
|
||||||
|
);
|
||||||
|
resizeObserver.observe(viewportEl);
|
||||||
|
|
||||||
|
tabItemCount = props.tabs?.length || 0;
|
||||||
|
mutationObserver?.disconnect();
|
||||||
|
// 使用 MutationObserver 仅监听子节点数量变化
|
||||||
|
mutationObserver = new MutationObserver(() => {
|
||||||
|
const count = viewportEl.querySelectorAll(
|
||||||
|
`div[data-tab-item="true"]`,
|
||||||
|
).length;
|
||||||
|
|
||||||
|
if (count > tabItemCount) {
|
||||||
|
scrollToActiveIntoView();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count !== tabItemCount) {
|
||||||
|
calcShowScrollbarButton();
|
||||||
|
tabItemCount = count;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 配置为仅监听子节点的添加和移除
|
||||||
|
mutationObserver.observe(viewportEl, {
|
||||||
|
attributes: false,
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function scrollToActiveIntoView() {
|
||||||
|
if (!scrollViewportEl.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await nextTick();
|
||||||
|
const viewportEl = scrollViewportEl.value;
|
||||||
|
const { scrollbarWidth } = getScrollClientWidth();
|
||||||
|
const { scrollWidth } = viewportEl;
|
||||||
|
|
||||||
|
if (scrollbarWidth >= scrollWidth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const activeItem = viewportEl?.querySelector('.is-active');
|
||||||
|
activeItem?.scrollIntoView({ behavior: 'smooth', inline: 'start' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算tabs 宽度,用于判断是否显示左右滚动按钮
|
||||||
|
*/
|
||||||
|
async function calcShowScrollbarButton() {
|
||||||
|
if (!scrollViewportEl.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { scrollbarWidth } = getScrollClientWidth();
|
||||||
|
|
||||||
|
showScrollButton.value =
|
||||||
|
scrollViewportEl.value.scrollWidth > scrollbarWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleScrollAt = useDebounceFn(({ left, right }) => {
|
||||||
|
scrollIsAtLeft.value = left;
|
||||||
|
scrollIsAtRight.value = right;
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.active,
|
||||||
|
async () => {
|
||||||
|
// 200为了等待 tab 切换动画完成
|
||||||
|
// setTimeout(() => {
|
||||||
|
scrollToActiveIntoView();
|
||||||
|
// }, 300);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
flush: 'post',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// watch(
|
||||||
|
// () => props.tabs?.length,
|
||||||
|
// async () => {
|
||||||
|
// await nextTick();
|
||||||
|
// calcShowScrollbarButton();
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// flush: 'post',
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.styleType,
|
||||||
|
() => {
|
||||||
|
initScrollbar();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(initScrollbar);
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
resizeObserver?.disconnect();
|
||||||
|
mutationObserver?.disconnect();
|
||||||
|
resizeObserver = null;
|
||||||
|
mutationObserver = null;
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
handleScrollAt,
|
||||||
initScrollbar,
|
initScrollbar,
|
||||||
|
scrollbarRef,
|
||||||
scrollDirection,
|
scrollDirection,
|
||||||
|
scrollIsAtLeft,
|
||||||
|
scrollIsAtRight,
|
||||||
|
showScrollButton,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,6 @@
|
||||||
"@vben/stores": "workspace:*",
|
"@vben/stores": "workspace:*",
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"@vben/utils": "workspace:*",
|
"@vben/utils": "workspace:*",
|
||||||
"vue": "^3.4.38"
|
"vue": "^3.4.37"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vben/preferences": "workspace:*",
|
"@vben/preferences": "workspace:*",
|
||||||
"@vueuse/core": "^10.11.1",
|
"@vueuse/core": "^11.0.0",
|
||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
"vue": "^3.4.38"
|
"vue": "^3.4.37"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,9 @@
|
||||||
"@vben/icons": "workspace:*",
|
"@vben/icons": "workspace:*",
|
||||||
"@vben/locales": "workspace:*",
|
"@vben/locales": "workspace:*",
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"@vueuse/integrations": "^10.11.1",
|
"@vueuse/integrations": "^11.0.0",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.4.37",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "^4.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
"@vben/stores": "workspace:*",
|
"@vben/stores": "workspace:*",
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"@vben/utils": "workspace:*",
|
"@vben/utils": "workspace:*",
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.4.37",
|
||||||
"vue-router": "^4.4.3",
|
"vue-router": "^4.4.3",
|
||||||
"watermark-js-plus": "^1.5.3"
|
"watermark-js-plus": "^1.5.3"
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,8 @@
|
||||||
"@vben/stores": "workspace:*",
|
"@vben/stores": "workspace:*",
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"@vben/utils": "workspace:*",
|
"@vben/utils": "workspace:*",
|
||||||
"@vueuse/core": "^10.11.1",
|
"@vueuse/core": "^11.0.0",
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.4.37",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "^4.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/core-base": "^9.13.1",
|
"@intlify/core-base": "^9.13.1",
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.4.37",
|
||||||
"vue-i18n": "^9.13.1"
|
"vue-i18n": "^9.13.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
"@vben-core/typings": "workspace:*",
|
"@vben-core/typings": "workspace:*",
|
||||||
"pinia": "2.2.2",
|
"pinia": "2.2.2",
|
||||||
"pinia-plugin-persistedstate": "^3.2.1",
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.4.37",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "^4.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,10 +124,21 @@ export const useTabbarStore = defineStore('core-tabbar', {
|
||||||
} else {
|
} else {
|
||||||
// 页面已经存在,不重复添加选项卡,只更新选项卡参数
|
// 页面已经存在,不重复添加选项卡,只更新选项卡参数
|
||||||
const currentTab = toRaw(this.tabs)[tabIndex];
|
const currentTab = toRaw(this.tabs)[tabIndex];
|
||||||
const mergedTab = { ...currentTab, ...tab };
|
const mergedTab = {
|
||||||
if (currentTab && Reflect.has(currentTab.meta, 'affixTab')) {
|
...currentTab,
|
||||||
mergedTab.meta.affixTab = currentTab.meta.affixTab;
|
...tab,
|
||||||
|
meta: { ...currentTab?.meta, ...tab.meta },
|
||||||
|
};
|
||||||
|
if (currentTab) {
|
||||||
|
const curMeta = currentTab.meta;
|
||||||
|
if (Reflect.has(curMeta, 'affixTab')) {
|
||||||
|
mergedTab.meta.affixTab = curMeta.affixTab;
|
||||||
|
}
|
||||||
|
if (Reflect.has(curMeta, 'newTabTitle')) {
|
||||||
|
mergedTab.meta.newTabTitle = curMeta.newTabTitle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tabs.splice(tabIndex, 1, mergedTab);
|
this.tabs.splice(tabIndex, 1, mergedTab);
|
||||||
}
|
}
|
||||||
this.updateCacheTab();
|
this.updateCacheTab();
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vben-core/typings": "workspace:*",
|
"@vben-core/typings": "workspace:*",
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.4.37",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "^4.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,11 +40,11 @@
|
||||||
"@vben/styles": "workspace:*",
|
"@vben/styles": "workspace:*",
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"@vben/utils": "workspace:*",
|
"@vben/utils": "workspace:*",
|
||||||
"@vueuse/core": "^10.11.1",
|
"@vueuse/core": "^11.0.0",
|
||||||
"ant-design-vue": "^4.2.3",
|
"ant-design-vue": "^4.2.3",
|
||||||
"dayjs": "^1.11.12",
|
"dayjs": "^1.11.12",
|
||||||
"pinia": "2.2.2",
|
"pinia": "2.2.2",
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.4.37",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "^4.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,7 +161,7 @@ const routes: RouteRecordRaw[] = [
|
||||||
import(
|
import(
|
||||||
'#/views/demos/features/hide-menu-children/children.vue'
|
'#/views/demos/features/hide-menu-children/children.vue'
|
||||||
),
|
),
|
||||||
meta: { title: 'HideChildrenInMenuChildrenDemo' },
|
meta: { title: $t('page.demos.features.hideChildrenInMenu') },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
115
pnpm-lock.yaml
115
pnpm-lock.yaml
|
@ -165,8 +165,8 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/utils
|
version: link:../../packages/utils
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.11.1
|
specifier: ^11.0.0
|
||||||
version: 10.11.1(vue@3.4.38(typescript@5.5.4))
|
version: 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
ant-design-vue:
|
ant-design-vue:
|
||||||
specifier: ^4.2.3
|
specifier: ^4.2.3
|
||||||
version: 4.2.3(vue@3.4.38(typescript@5.5.4))
|
version: 4.2.3(vue@3.4.38(typescript@5.5.4))
|
||||||
|
@ -228,8 +228,8 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/utils
|
version: link:../../packages/utils
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.11.1
|
specifier: ^11.0.0
|
||||||
version: 10.11.1(vue@3.4.38(typescript@5.5.4))
|
version: 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
dayjs:
|
dayjs:
|
||||||
specifier: ^1.11.12
|
specifier: ^1.11.12
|
||||||
version: 1.11.12
|
version: 1.11.12
|
||||||
|
@ -295,8 +295,8 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/utils
|
version: link:../../packages/utils
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.11.1
|
specifier: ^11.0.0
|
||||||
version: 10.11.1(vue@3.4.38(typescript@5.5.4))
|
version: 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
naive-ui:
|
naive-ui:
|
||||||
specifier: ^2.39.0
|
specifier: ^2.39.0
|
||||||
version: 2.39.0(vue@3.4.38(typescript@5.5.4))
|
version: 2.39.0(vue@3.4.38(typescript@5.5.4))
|
||||||
|
@ -660,8 +660,8 @@ importers:
|
||||||
specifier: ^4.1.2
|
specifier: ^4.1.2
|
||||||
version: 4.1.2(vue@3.4.38(typescript@5.5.4))
|
version: 4.1.2(vue@3.4.38(typescript@5.5.4))
|
||||||
lucide-vue-next:
|
lucide-vue-next:
|
||||||
specifier: ^0.427.0
|
specifier: ^0.428.0
|
||||||
version: 0.427.0(vue@3.4.38(typescript@5.5.4))
|
version: 0.428.0(vue@3.4.38(typescript@5.5.4))
|
||||||
vue:
|
vue:
|
||||||
specifier: 3.4.38
|
specifier: 3.4.38
|
||||||
version: 3.4.38(typescript@5.5.4)
|
version: 3.4.38(typescript@5.5.4)
|
||||||
|
@ -672,7 +672,7 @@ importers:
|
||||||
specifier: 4.1.0
|
specifier: 4.1.0
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
'@vue/shared':
|
'@vue/shared':
|
||||||
specifier: ^3.4.38
|
specifier: ^3.4.37
|
||||||
version: 3.4.38
|
version: 3.4.38
|
||||||
clsx:
|
clsx:
|
||||||
specifier: 2.1.1
|
specifier: 2.1.1
|
||||||
|
@ -715,8 +715,8 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../base/shared
|
version: link:../base/shared
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.11.1
|
specifier: ^11.0.0
|
||||||
version: 10.11.1(vue@3.4.38(typescript@5.5.4))
|
version: 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
radix-vue:
|
radix-vue:
|
||||||
specifier: ^1.9.4
|
specifier: ^1.9.4
|
||||||
version: 1.9.4(vue@3.4.38(typescript@5.5.4))
|
version: 1.9.4(vue@3.4.38(typescript@5.5.4))
|
||||||
|
@ -740,8 +740,8 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../base/typings
|
version: link:../base/typings
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.11.1
|
specifier: ^11.0.0
|
||||||
version: 10.11.1(vue@3.4.38(typescript@5.5.4))
|
version: 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
vue:
|
vue:
|
||||||
specifier: 3.4.38
|
specifier: 3.4.38
|
||||||
version: 3.4.38(typescript@5.5.4)
|
version: 3.4.38(typescript@5.5.4)
|
||||||
|
@ -761,8 +761,8 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../base/typings
|
version: link:../../base/typings
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.11.1
|
specifier: ^11.0.0
|
||||||
version: 10.11.1(vue@3.4.38(typescript@5.5.4))
|
version: 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
vue:
|
vue:
|
||||||
specifier: 3.4.38
|
specifier: 3.4.38
|
||||||
version: 3.4.38(typescript@5.5.4)
|
version: 3.4.38(typescript@5.5.4)
|
||||||
|
@ -785,8 +785,8 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../base/typings
|
version: link:../../base/typings
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.11.1
|
specifier: ^11.0.0
|
||||||
version: 10.11.1(vue@3.4.38(typescript@5.5.4))
|
version: 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
vue:
|
vue:
|
||||||
specifier: 3.4.38
|
specifier: 3.4.38
|
||||||
version: 3.4.38(typescript@5.5.4)
|
version: 3.4.38(typescript@5.5.4)
|
||||||
|
@ -806,14 +806,14 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../base/typings
|
version: link:../../base/typings
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.11.1
|
specifier: ^11.0.0
|
||||||
version: 10.11.1(vue@3.4.38(typescript@5.5.4))
|
version: 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
class-variance-authority:
|
class-variance-authority:
|
||||||
specifier: ^0.7.0
|
specifier: ^0.7.0
|
||||||
version: 0.7.0
|
version: 0.7.0
|
||||||
lucide-vue-next:
|
lucide-vue-next:
|
||||||
specifier: ^0.427.0
|
specifier: ^0.428.0
|
||||||
version: 0.427.0(vue@3.4.38(typescript@5.5.4))
|
version: 0.428.0(vue@3.4.38(typescript@5.5.4))
|
||||||
radix-vue:
|
radix-vue:
|
||||||
specifier: ^1.9.4
|
specifier: ^1.9.4
|
||||||
version: 1.9.4(vue@3.4.38(typescript@5.5.4))
|
version: 1.9.4(vue@3.4.38(typescript@5.5.4))
|
||||||
|
@ -835,6 +835,9 @@ importers:
|
||||||
'@vben-core/typings':
|
'@vben-core/typings':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../base/typings
|
version: link:../../base/typings
|
||||||
|
'@vueuse/core':
|
||||||
|
specifier: ^11.0.0
|
||||||
|
version: 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
vue:
|
vue:
|
||||||
specifier: 3.4.38
|
specifier: 3.4.38
|
||||||
version: 3.4.38(typescript@5.5.4)
|
version: 3.4.38(typescript@5.5.4)
|
||||||
|
@ -869,8 +872,8 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../preferences
|
version: link:../../preferences
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.11.1
|
specifier: ^11.0.0
|
||||||
version: 10.11.1(vue@3.4.38(typescript@5.5.4))
|
version: 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
echarts:
|
echarts:
|
||||||
specifier: ^5.5.1
|
specifier: ^5.5.1
|
||||||
version: 5.5.1
|
version: 5.5.1
|
||||||
|
@ -899,8 +902,8 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../types
|
version: link:../../types
|
||||||
'@vueuse/integrations':
|
'@vueuse/integrations':
|
||||||
specifier: ^10.11.1
|
specifier: ^11.0.0
|
||||||
version: 10.11.1(async-validator@4.2.5)(axios@1.7.4)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.2)(vue@3.4.38(typescript@5.5.4))
|
version: 11.0.0(async-validator@4.2.5)(axios@1.7.4)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.2)(vue@3.4.38(typescript@5.5.4))
|
||||||
qrcode:
|
qrcode:
|
||||||
specifier: ^1.5.4
|
specifier: ^1.5.4
|
||||||
version: 1.5.4
|
version: 1.5.4
|
||||||
|
@ -981,8 +984,8 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../utils
|
version: link:../../utils
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.11.1
|
specifier: ^11.0.0
|
||||||
version: 10.11.1(vue@3.4.38(typescript@5.5.4))
|
version: 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
vue:
|
vue:
|
||||||
specifier: 3.4.38
|
specifier: 3.4.38
|
||||||
version: 3.4.38(typescript@5.5.4)
|
version: 3.4.38(typescript@5.5.4)
|
||||||
|
@ -1129,8 +1132,8 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../packages/utils
|
version: link:../packages/utils
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^10.11.1
|
specifier: ^11.0.0
|
||||||
version: 10.11.1(vue@3.4.38(typescript@5.5.4))
|
version: 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
ant-design-vue:
|
ant-design-vue:
|
||||||
specifier: ^4.2.3
|
specifier: ^4.2.3
|
||||||
version: 4.2.3(vue@3.4.38(typescript@5.5.4))
|
version: 4.2.3(vue@3.4.38(typescript@5.5.4))
|
||||||
|
@ -4179,6 +4182,9 @@ packages:
|
||||||
'@vueuse/core@10.11.1':
|
'@vueuse/core@10.11.1':
|
||||||
resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==}
|
resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==}
|
||||||
|
|
||||||
|
'@vueuse/core@11.0.0':
|
||||||
|
resolution: {integrity: sha512-shibzNGjmRjZucEm97B8V0NO5J3vPHMCE/mltxQ3vHezbDoFQBMtK11XsfwfPionxSbo+buqPmsCljtYuXIBpw==}
|
||||||
|
|
||||||
'@vueuse/core@9.13.0':
|
'@vueuse/core@9.13.0':
|
||||||
resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==}
|
resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==}
|
||||||
|
|
||||||
|
@ -4223,21 +4229,21 @@ packages:
|
||||||
universal-cookie:
|
universal-cookie:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@vueuse/integrations@10.11.1':
|
'@vueuse/integrations@11.0.0':
|
||||||
resolution: {integrity: sha512-Y5hCGBguN+vuVYTZmdd/IMXLOdfS60zAmDmFYc4BKBcMUPZH1n4tdyDECCPjXm0bNT3ZRUy1xzTLGaUje8Xyaw==}
|
resolution: {integrity: sha512-B95nBX4B2q2ZETBDldrKARM/fYXBHfwdo44UbHBq4bUTi25lrlc8MwAZGqEoRvdV4ND9T6O1Rb9e4kaCJFXnqw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
async-validator: ^4
|
async-validator: ^4
|
||||||
axios: ^1
|
axios: ^1
|
||||||
change-case: ^4
|
change-case: ^5
|
||||||
drauu: ^0.3
|
drauu: ^0.4
|
||||||
focus-trap: ^7
|
focus-trap: ^7
|
||||||
fuse.js: ^6
|
fuse.js: ^7
|
||||||
idb-keyval: ^6
|
idb-keyval: ^6
|
||||||
jwt-decode: ^3
|
jwt-decode: ^4
|
||||||
nprogress: ^0.2
|
nprogress: ^0.2
|
||||||
qrcode: ^1.5
|
qrcode: ^1.5
|
||||||
sortablejs: ^1
|
sortablejs: ^1
|
||||||
universal-cookie: ^6
|
universal-cookie: ^7
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
async-validator:
|
async-validator:
|
||||||
optional: true
|
optional: true
|
||||||
|
@ -4270,6 +4276,9 @@ packages:
|
||||||
'@vueuse/metadata@10.11.1':
|
'@vueuse/metadata@10.11.1':
|
||||||
resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==}
|
resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==}
|
||||||
|
|
||||||
|
'@vueuse/metadata@11.0.0':
|
||||||
|
resolution: {integrity: sha512-0TKsAVT0iUOAPWyc9N79xWYfovJVPATiOPVKByG6jmAYdDiwvMVm9xXJ5hp4I8nZDxpCcYlLq/Rg9w1Z/jrGcg==}
|
||||||
|
|
||||||
'@vueuse/metadata@9.13.0':
|
'@vueuse/metadata@9.13.0':
|
||||||
resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
|
resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
|
||||||
|
|
||||||
|
@ -4279,6 +4288,9 @@ packages:
|
||||||
'@vueuse/shared@10.11.1':
|
'@vueuse/shared@10.11.1':
|
||||||
resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==}
|
resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==}
|
||||||
|
|
||||||
|
'@vueuse/shared@11.0.0':
|
||||||
|
resolution: {integrity: sha512-i4ZmOrIEjSsL94uAEt3hz88UCz93fMyP/fba9S+vypX90fKg3uYX9cThqvWc9aXxuTzR0UGhOKOTQd//Goh1nQ==}
|
||||||
|
|
||||||
'@vueuse/shared@9.13.0':
|
'@vueuse/shared@9.13.0':
|
||||||
resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
|
resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
|
||||||
|
|
||||||
|
@ -6916,8 +6928,8 @@ packages:
|
||||||
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
lucide-vue-next@0.427.0:
|
lucide-vue-next@0.428.0:
|
||||||
resolution: {integrity: sha512-zI1FhbfQ3Wl0SgPKnOWhTDC6yAC5TTjSC9FSZ61ULg3U36e+GVK+RT1qfkU9Q5BjeBuwmsHWKsXKptKMjUAwFA==}
|
resolution: {integrity: sha512-of9GJGus9VKGIUOp3yQ0uQtNv+8MRLaso8H4OiDzI6+T7TeMRXTzqVOLhnyg9fdXUnYuwE9Xm1zD1nfQ7oFPmg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: 3.4.38
|
vue: 3.4.38
|
||||||
|
|
||||||
|
@ -13196,6 +13208,16 @@ snapshots:
|
||||||
- '@vue/composition-api'
|
- '@vue/composition-api'
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
|
'@vueuse/core@11.0.0(vue@3.4.38(typescript@5.5.4))':
|
||||||
|
dependencies:
|
||||||
|
'@types/web-bluetooth': 0.0.20
|
||||||
|
'@vueuse/metadata': 11.0.0
|
||||||
|
'@vueuse/shared': 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
|
vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4))
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@vue/composition-api'
|
||||||
|
- vue
|
||||||
|
|
||||||
'@vueuse/core@9.13.0(vue@3.4.38(typescript@5.5.4))':
|
'@vueuse/core@9.13.0(vue@3.4.38(typescript@5.5.4))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/web-bluetooth': 0.0.16
|
'@types/web-bluetooth': 0.0.16
|
||||||
|
@ -13222,10 +13244,10 @@ snapshots:
|
||||||
- '@vue/composition-api'
|
- '@vue/composition-api'
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
'@vueuse/integrations@10.11.1(async-validator@4.2.5)(axios@1.7.4)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.2)(vue@3.4.38(typescript@5.5.4))':
|
'@vueuse/integrations@11.0.0(async-validator@4.2.5)(axios@1.7.4)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.2)(vue@3.4.38(typescript@5.5.4))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4))
|
'@vueuse/core': 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
'@vueuse/shared': 10.11.1(vue@3.4.38(typescript@5.5.4))
|
'@vueuse/shared': 11.0.0(vue@3.4.38(typescript@5.5.4))
|
||||||
vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4))
|
vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4))
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
async-validator: 4.2.5
|
async-validator: 4.2.5
|
||||||
|
@ -13242,6 +13264,8 @@ snapshots:
|
||||||
|
|
||||||
'@vueuse/metadata@10.11.1': {}
|
'@vueuse/metadata@10.11.1': {}
|
||||||
|
|
||||||
|
'@vueuse/metadata@11.0.0': {}
|
||||||
|
|
||||||
'@vueuse/metadata@9.13.0': {}
|
'@vueuse/metadata@9.13.0': {}
|
||||||
|
|
||||||
'@vueuse/shared@10.11.0(vue@3.4.38(typescript@5.5.4))':
|
'@vueuse/shared@10.11.0(vue@3.4.38(typescript@5.5.4))':
|
||||||
|
@ -13258,6 +13282,13 @@ snapshots:
|
||||||
- '@vue/composition-api'
|
- '@vue/composition-api'
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
|
'@vueuse/shared@11.0.0(vue@3.4.38(typescript@5.5.4))':
|
||||||
|
dependencies:
|
||||||
|
vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4))
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@vue/composition-api'
|
||||||
|
- vue
|
||||||
|
|
||||||
'@vueuse/shared@9.13.0(vue@3.4.38(typescript@5.5.4))':
|
'@vueuse/shared@9.13.0(vue@3.4.38(typescript@5.5.4))':
|
||||||
dependencies:
|
dependencies:
|
||||||
vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4))
|
vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4))
|
||||||
|
@ -16250,7 +16281,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
yallist: 4.0.0
|
yallist: 4.0.0
|
||||||
|
|
||||||
lucide-vue-next@0.427.0(vue@3.4.38(typescript@5.5.4)):
|
lucide-vue-next@0.428.0(vue@3.4.38(typescript@5.5.4)):
|
||||||
dependencies:
|
dependencies:
|
||||||
vue: 3.4.38(typescript@5.5.4)
|
vue: 3.4.38(typescript@5.5.4)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue