chore: update uikit -> ui-kit
This commit is contained in:
47
packages/@core/ui-kit/tabs-ui/package.json
Normal file
47
packages/@core/ui-kit/tabs-ui/package.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "@vben-core/tabs-ui",
|
||||
"version": "5.0.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||
"directory": "packages/@vben-core/uikit/tabs-ui"
|
||||
},
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "pnpm vite build",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"sideEffects": [
|
||||
"**/*.css"
|
||||
],
|
||||
"main": "./dist/index.mjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"development": "./src/index.ts",
|
||||
"default": "./dist/index.mjs"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
".": {
|
||||
"default": "./dist/index.mjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@vben-core/design": "workspace:*",
|
||||
"@vben-core/iconify": "workspace:*",
|
||||
"@vben-core/shadcn-ui": "workspace:*",
|
||||
"@vben-core/toolkit": "workspace:*",
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"vue": "^3.4.30"
|
||||
}
|
||||
}
|
1
packages/@core/ui-kit/tabs-ui/postcss.config.mjs
Normal file
1
packages/@core/ui-kit/tabs-ui/postcss.config.mjs
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from '@vben/tailwind-config/postcss';
|
@@ -0,0 +1,193 @@
|
||||
@import '@vben-core/design/global';
|
||||
|
||||
@include b('chrome-tabs') {
|
||||
--tabs-background: hsl(var(--background));
|
||||
--tabs-gap: 7px;
|
||||
--tabs-divider: hsl(var(--border));
|
||||
--tabs-hover: hsl(var(--heavy));
|
||||
--tabs-active-background: hsl(var(--primary) / 100%);
|
||||
--tabs-active: hsl(var(--primary-foreground));
|
||||
|
||||
background-color: var(--tabs-background);
|
||||
}
|
||||
|
||||
@include b('chrome-tab') {
|
||||
color: hsl(var(--muted-foreground));
|
||||
|
||||
@include is('active') {
|
||||
z-index: 2;
|
||||
color: var(--tabs-active);
|
||||
|
||||
.#{$namespace}-chrome-tab__extra:not(.is-pin) {
|
||||
background-color: var(--tabs-active-background);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.#{$namespace}-chrome-tab-background__divider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.#{$namespace}-chrome-tab-background__content {
|
||||
background-color: var(--tabs-active-background);
|
||||
}
|
||||
|
||||
.#{$namespace}-chrome-tab-background__before,
|
||||
.#{$namespace}-chrome-tab-background__after {
|
||||
fill: var(--tabs-active-background);
|
||||
}
|
||||
}
|
||||
|
||||
@include e('content') {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding-right: 10px;
|
||||
margin: 0 calc(var(--tabs-gap) * 2);
|
||||
overflow: hidden;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
}
|
||||
|
||||
@include e('extra') {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: calc(var(--tabs-gap) * 2);
|
||||
z-index: 1;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
transition: 0.15s;
|
||||
transform: translateY(-50%);
|
||||
|
||||
// &:hover {
|
||||
// background-color: hsl(var(--accent));
|
||||
// }
|
||||
}
|
||||
|
||||
@include e('extra-icon') {
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
border-radius: 50%;
|
||||
transition: all 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
color: hsl(var(--foreground));
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
@include e('icon') {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 16px;
|
||||
margin-left: 3%;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@include e('label') {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
margin-right: 8px;
|
||||
margin-left: 5%;
|
||||
overflow: hidden;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
mask-image: linear-gradient(
|
||||
90deg,
|
||||
#000 0%,
|
||||
#000 calc(100% - 20px),
|
||||
transparent
|
||||
);
|
||||
|
||||
// &.no-close {
|
||||
// margin-right: 0;
|
||||
// }
|
||||
|
||||
// &.no-icon {
|
||||
// margin-left: 0;
|
||||
// }
|
||||
}
|
||||
|
||||
@include is('hidden-icon') {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.#{$namespace}-chrome-tab__extra.is-pin {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.is-active):hover {
|
||||
z-index: 1;
|
||||
|
||||
.#{$namespace}-chrome-tab__extra {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.#{$namespace}-chrome-tab-background__divider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.#{$namespace}-chrome-tab-background__content {
|
||||
background-color: var(--tabs-hover);
|
||||
}
|
||||
|
||||
.#{$namespace}-chrome-tab-background__before,
|
||||
.#{$namespace}-chrome-tab-background__after {
|
||||
fill: var(--tabs-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&:first-of-type {
|
||||
.#{$namespace}-chrome-tab-background__divider::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include b('chrome-tab-background') {
|
||||
padding: 0 calc(var(--tabs-gap) + 0px);
|
||||
|
||||
@include e('divider') {
|
||||
width: calc(100% - 14px);
|
||||
margin: 0 7px;
|
||||
|
||||
&::before {
|
||||
background-color: var(--tabs-divider);
|
||||
}
|
||||
|
||||
&::after {
|
||||
left: calc(100% - 1px);
|
||||
background-color: var(--tabs-divider);
|
||||
}
|
||||
}
|
||||
|
||||
@include e('content') {
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
|
||||
@include e('before') {
|
||||
bottom: -1px;
|
||||
left: -3px;
|
||||
transition: 0.15s;
|
||||
}
|
||||
|
||||
@include e('after') {
|
||||
right: -3px;
|
||||
bottom: -1px;
|
||||
transition: 0.15s;
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import { useNamespace } from '@vben-core/toolkit';
|
||||
|
||||
defineOptions({
|
||||
name: 'ChromeTabBackground',
|
||||
});
|
||||
|
||||
const { b, e } = useNamespace('chrome-tab-background');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="b()" class="absolute size-full">
|
||||
<div
|
||||
:class="e('divider')"
|
||||
class="absolute left-0 h-full before:absolute before:right-[100%] before:top-[15%] before:h-[60%] before:w-[1px] before:content-[''] after:absolute after:top-[15%] after:h-[60%] after:w-[1px] after:content-['']"
|
||||
></div>
|
||||
<div :class="e('content')" class="h-full"></div>
|
||||
<svg
|
||||
:class="e('before')"
|
||||
class="absolute fill-transparent"
|
||||
height="10"
|
||||
width="10"
|
||||
>
|
||||
<path d="M 0 10 A 10 10 0 0 0 10 0 L 10 10 Z" />
|
||||
</svg>
|
||||
<svg
|
||||
:class="e('after')"
|
||||
class="absolute fill-transparent"
|
||||
height="10"
|
||||
width="10"
|
||||
>
|
||||
<path d="M 0 0 A 10 10 0 0 0 10 10 L 0 10 Z" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
@@ -0,0 +1,71 @@
|
||||
<script setup lang="ts">
|
||||
import type { IContextMenuItem } from '@vben-core/shadcn-ui';
|
||||
import type { TabItem } from '@vben-core/typings';
|
||||
|
||||
import { IcRoundClose, MdiPin } from '@vben-core/iconify';
|
||||
import { VbenContextMenu, VbenIcon } from '@vben-core/shadcn-ui';
|
||||
import { useNamespace } from '@vben-core/toolkit';
|
||||
|
||||
import TabBackground from './tab-background.vue';
|
||||
|
||||
interface Props {
|
||||
affixTab?: boolean;
|
||||
icon?: string;
|
||||
menus: (data: any) => IContextMenuItem[];
|
||||
onlyOne?: boolean;
|
||||
showIcon?: boolean;
|
||||
tab: TabItem;
|
||||
title: string;
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
name: 'ChromeTab',
|
||||
});
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
icon: '',
|
||||
});
|
||||
const emit = defineEmits<{ close: []; unPushPin: [] }>();
|
||||
|
||||
const { b, e, is } = useNamespace('chrome-tab');
|
||||
|
||||
function handleClose() {
|
||||
emit('close');
|
||||
}
|
||||
function handleUnPushPin() {
|
||||
emit('unPushPin');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="[b()]"
|
||||
class="absolute flex h-full cursor-pointer select-none items-center"
|
||||
>
|
||||
<VbenContextMenu :handler-data="tab" :menus="menus" item-class="pr-4">
|
||||
<div class="h-full">
|
||||
<TabBackground />
|
||||
<div :class="e('content')" :title="title">
|
||||
<VbenIcon v-if="showIcon" :class="e('icon')" :icon="icon" fallback />
|
||||
<span :class="[e('label'), is('hidden-icon', !icon)]">
|
||||
{{ title }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-show="!affixTab && !onlyOne"
|
||||
:class="e('extra')"
|
||||
@click.stop="handleClose"
|
||||
>
|
||||
<IcRoundClose :class="e('extra-icon')" />
|
||||
</div>
|
||||
<div
|
||||
v-show="affixTab && !onlyOne"
|
||||
:class="[e('extra'), is('pin', true)]"
|
||||
@click.stop="handleUnPushPin"
|
||||
>
|
||||
<MdiPin :class="e('extra-icon')" />
|
||||
</div>
|
||||
</div>
|
||||
</VbenContextMenu>
|
||||
</div>
|
||||
</template>
|
@@ -0,0 +1,118 @@
|
||||
<script setup lang="ts">
|
||||
import type { TabItem } from '@vben-core/typings';
|
||||
|
||||
import type { TabsProps } from '../../interface';
|
||||
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { useNamespace } from '@vben-core/toolkit';
|
||||
|
||||
import Tab from './tab.vue';
|
||||
|
||||
interface Props extends TabsProps {}
|
||||
|
||||
defineOptions({
|
||||
name: 'ChromeTabs',
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
maxWidth: 150,
|
||||
menus: () => [],
|
||||
minWidth: 40,
|
||||
tabs: () => [],
|
||||
});
|
||||
|
||||
const emit = defineEmits<{ close: [string]; unPushPin: [TabItem] }>();
|
||||
|
||||
const gap = 7;
|
||||
|
||||
const active = defineModel<string>('active');
|
||||
const { b, e, is } = useNamespace('chrome-tabs');
|
||||
|
||||
const contentRef = ref();
|
||||
const tabWidth = ref<number>(0);
|
||||
|
||||
const layout = () => {
|
||||
const { maxWidth, minWidth, tabs } = props;
|
||||
if (!contentRef.value) {
|
||||
return Math.max(maxWidth, minWidth);
|
||||
}
|
||||
const contentWidth = contentRef.value.clientWidth - gap * 3;
|
||||
let width = contentWidth / tabs.length;
|
||||
width += gap * 2;
|
||||
if (width > maxWidth) {
|
||||
width = maxWidth;
|
||||
}
|
||||
if (width < minWidth) {
|
||||
width = minWidth;
|
||||
}
|
||||
tabWidth.value = width;
|
||||
};
|
||||
|
||||
const tabsView = computed(() => {
|
||||
return props.tabs.map((tab) => {
|
||||
return {
|
||||
...tab,
|
||||
affixTab: !!tab.meta?.affixTab,
|
||||
icon: tab.meta.icon as string,
|
||||
key: tab.fullPath || tab.path,
|
||||
title: (tab.meta?.title || tab.name) as string,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.tabs,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
layout();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
layout();
|
||||
});
|
||||
|
||||
function handleClose(key: string) {
|
||||
emit('close', key);
|
||||
}
|
||||
function handleUnPushPin(tab: TabItem) {
|
||||
emit('unPushPin', tab);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="b()" class="relative size-full pt-1">
|
||||
<div
|
||||
ref="contentRef"
|
||||
:class="e('content')"
|
||||
class="relative h-8 overflow-hidden"
|
||||
>
|
||||
<TransitionGroup name="slide-down">
|
||||
<Tab
|
||||
v-for="(tab, i) in tabsView"
|
||||
:key="tab.key"
|
||||
:affix-tab="tab.affixTab"
|
||||
:class="[e('tab'), is('active', tab.key === active)]"
|
||||
:icon="tab.icon"
|
||||
:menus="menus"
|
||||
:only-one="tabsView.length <= 1"
|
||||
:show-icon="showIcon"
|
||||
:style="{
|
||||
width: `${tabWidth}px`,
|
||||
left: `${(tabWidth - gap * 2) * i}px`,
|
||||
}"
|
||||
:tab="tab"
|
||||
:title="tab.title"
|
||||
@click="active = tab.key"
|
||||
@close="() => handleClose(tab.key)"
|
||||
@un-push-pin="() => handleUnPushPin(tab)"
|
||||
/>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
@import './chrome-tabs.scss';
|
||||
</style>
|
1
packages/@core/ui-kit/tabs-ui/src/components/index.ts
Normal file
1
packages/@core/ui-kit/tabs-ui/src/components/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as ChromeTabs } from './chrome-tabs/tabs.vue';
|
@@ -0,0 +1,5 @@
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
3
packages/@core/ui-kit/tabs-ui/src/index.ts
Normal file
3
packages/@core/ui-kit/tabs-ui/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as TabsView } from './tabs-view.vue';
|
||||
export * from './widgets';
|
||||
export type { IContextMenuItem } from '@vben-core/shadcn-ui';
|
12
packages/@core/ui-kit/tabs-ui/src/interface.ts
Normal file
12
packages/@core/ui-kit/tabs-ui/src/interface.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { IContextMenuItem } from '@vben-core/shadcn-ui';
|
||||
import type { TabItem } from '@vben-core/typings';
|
||||
|
||||
interface TabsProps {
|
||||
maxWidth?: number;
|
||||
menus?: (data: any) => IContextMenuItem[];
|
||||
minWidth?: number;
|
||||
showIcon?: boolean;
|
||||
tabs?: TabItem[];
|
||||
}
|
||||
|
||||
export type { TabsProps };
|
24
packages/@core/ui-kit/tabs-ui/src/tabs-view.vue
Normal file
24
packages/@core/ui-kit/tabs-ui/src/tabs-view.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import type { TabItem } from '@vben-core/typings';
|
||||
|
||||
import { useForwardPropsEmits } from '@vben-core/shadcn-ui';
|
||||
|
||||
import { ChromeTabs } from './components';
|
||||
import { TabsProps } from './interface';
|
||||
|
||||
interface Props extends TabsProps {}
|
||||
|
||||
defineOptions({
|
||||
name: 'TabsView',
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
|
||||
const emit = defineEmits<{ close: [string]; unPushPin: [TabItem] }>();
|
||||
|
||||
const forward = useForwardPropsEmits(props, emit);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ChromeTabs v-bind="forward" />
|
||||
</template>
|
2
packages/@core/ui-kit/tabs-ui/src/widgets/index.ts
Normal file
2
packages/@core/ui-kit/tabs-ui/src/widgets/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as TabsToolMore } from './tool-more.vue';
|
||||
export { default as TabsToolScreen } from './tool-screen.vue';
|
18
packages/@core/ui-kit/tabs-ui/src/widgets/tool-more.vue
Normal file
18
packages/@core/ui-kit/tabs-ui/src/widgets/tool-more.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<script lang="ts" setup>
|
||||
import type { DropdownMenuProps } from '@vben-core/shadcn-ui';
|
||||
|
||||
import { IcRoundMoreVert } from '@vben-core/iconify';
|
||||
import { VbenDropdownMenu } from '@vben-core/shadcn-ui';
|
||||
|
||||
defineProps<DropdownMenuProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VbenDropdownMenu :menus="menus">
|
||||
<div
|
||||
class="flex-center hover:bg-accent hover:text-foreground text-muted-foreground border-border h-full cursor-pointer border-l px-2 text-lg font-semibold"
|
||||
>
|
||||
<IcRoundMoreVert />
|
||||
</div>
|
||||
</VbenDropdownMenu>
|
||||
</template>
|
19
packages/@core/ui-kit/tabs-ui/src/widgets/tool-screen.vue
Normal file
19
packages/@core/ui-kit/tabs-ui/src/widgets/tool-screen.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
import { IcRoundFitScreen, IcTwotoneFitScreen } from '@vben-core/iconify';
|
||||
|
||||
const screen = defineModel<boolean>('screen');
|
||||
|
||||
function toggleScreen() {
|
||||
screen.value = !screen.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex-center hover:bg-accent hover:text-foreground text-muted-foreground border-border h-full cursor-pointer border-l px-2 text-lg font-semibold"
|
||||
@click="toggleScreen"
|
||||
>
|
||||
<IcTwotoneFitScreen v-if="screen" />
|
||||
<IcRoundFitScreen v-else />
|
||||
</div>
|
||||
</template>
|
1
packages/@core/ui-kit/tabs-ui/tailwind.config.mjs
Normal file
1
packages/@core/ui-kit/tabs-ui/tailwind.config.mjs
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from '@vben/tailwind-config';
|
6
packages/@core/ui-kit/tabs-ui/tsconfig.json
Normal file
6
packages/@core/ui-kit/tabs-ui/tsconfig.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/web.json",
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
3
packages/@core/ui-kit/tabs-ui/vite.config.mts
Normal file
3
packages/@core/ui-kit/tabs-ui/vite.config.mts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { defineConfig } from '@vben/vite-config';
|
||||
|
||||
export default defineConfig();
|
Reference in New Issue
Block a user