refactor(project): remove the use of core internal bem syntax

This commit is contained in:
vben
2024-07-14 16:28:56 +08:00
parent fed422e187
commit ebf73b2df9
37 changed files with 730 additions and 173 deletions

View File

@@ -0,0 +1,33 @@
import { defineBuildConfig } from 'unbuild';
export default defineBuildConfig({
clean: true,
declaration: true,
entries: [
{
builder: 'mkdist',
input: './src',
pattern: ['**/*'],
},
{
builder: 'mkdist',
input: './src',
loaders: ['vue'],
pattern: ['**/*.vue'],
},
// {
// builder: 'mkdist',
// format: 'cjs',
// input: './src',
// loaders: ['js'],
// pattern: ['**/*.ts'],
// },
{
builder: 'mkdist',
format: 'esm',
input: './src',
loaders: ['js'],
pattern: ['**/*.ts'],
},
],
});

View File

@@ -11,7 +11,7 @@
"license": "MIT",
"type": "module",
"scripts": {
"build": "pnpm vite build",
"build": "pnpm unbuild",
"prepublishOnly": "npm run build"
},
"files": [
@@ -37,7 +37,7 @@
}
},
"dependencies": {
"@vben-core/design": "workspace:*",
"@vben-core/hooks": "workspace:*",
"@vben-core/iconify": "workspace:*",
"@vben-core/shadcn-ui": "workspace:*",
"@vben-core/toolkit": "workspace:*",

View File

@@ -3,8 +3,8 @@ import type { MenuItemProps, MenuItemRegistered } from '../interface';
import { computed, onBeforeUnmount, onMounted, reactive, useSlots } from 'vue';
import { useNamespace } from '@vben-core/hooks';
import { VbenIcon, VbenMenuBadge, VbenTooltip } from '@vben-core/shadcn-ui';
import { useNamespace } from '@vben-core/toolkit';
import { useMenu, useMenuContext, useSubMenuContext } from '../hooks';

View File

@@ -18,8 +18,9 @@ import {
watchEffect,
} from 'vue';
import { useNamespace } from '@vben-core/hooks';
import { IcRoundMoreHoriz } from '@vben-core/iconify';
import { isHttpUrl, useNamespace } from '@vben-core/toolkit';
import { isHttpUrl } from '@vben-core/toolkit';
import { UseResizeObserverReturn, useResizeObserver } from '@vueuse/core';
@@ -348,3 +349,512 @@ function removeMenuItem(item: MenuItemRegistered) {
</template>
</ul>
</template>
<style lang="scss">
$namespace: vben;
@mixin menu-item-active {
color: var(--menu-item-active-color);
text-decoration: none;
cursor: pointer;
background: var(--menu-item-active-background-color);
}
@mixin menu-item {
position: relative;
display: flex;
// gap: 12px;
align-items: center;
height: var(--menu-item-height);
padding: var(--menu-item-padding-y) var(--menu-item-padding-x);
margin: 0 var(--menu-item-margin-x) var(--menu-item-margin-y)
var(--menu-item-margin-x);
font-size: var(--menu-font-size);
color: var(--menu-item-color);
text-decoration: none;
white-space: nowrap;
list-style: none;
cursor: pointer;
background: var(--menu-item-background-color);
border: none;
border-radius: var(--menu-item-radius);
transition:
background 0.15s ease,
color 0.15s ease,
padding 0.15s ease,
border-color 0.15s ease;
&.is-disabled {
cursor: not-allowed;
background: none !important;
opacity: 0.25;
}
.#{$namespace}-menu__icon {
transition: transform 0.25s;
}
&:hover {
.#{$namespace}-menu__icon {
transform: scale(1.3);
}
}
&:hover,
&:focus {
outline: none;
}
* {
vertical-align: bottom;
}
}
@mixin menu-title {
max-width: var(--menu-title-width);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
opacity: 1;
}
.#{$namespace}-menu__popup-container,
.#{$namespace}-menu {
--menu-title-width: 140px;
--menu-item-icon-width: 20px;
--menu-item-height: 38px;
--menu-item-padding-y: 26px;
--menu-item-padding-x: 12px;
--menu-item-popup-padding-y: 22px;
--menu-item-popup-padding-x: 12px;
--menu-item-margin-y: 4px;
--menu-item-margin-x: 0px;
--menu-item-collapse-padding-y: 25px;
--menu-item-collapse-padding-x: 0px;
--menu-item-collapse-margin-y: 4px;
--menu-item-collapse-margin-x: 0px;
--menu-item-radius: 0px;
--menu-item-indent: 16px;
--menu-font-size: 14px;
--menu-dark-background: 0deg 0% 100% / 10%;
--menu-light-background: 192deg 1% 93%;
&.is-dark {
--menu-background-color: hsl(var(--menu-dark));
// --menu-submenu-opened-background-color: hsl(var(--menu-opened-dark));
--menu-item-background-color: var(--menu-background-color);
--menu-item-color: hsl(var(--dark-foreground) / 80%);
--menu-item-hover-color: hsl(var(--primary-foreground));
--menu-item-hover-background-color: hsl(var(--menu-dark-background));
--menu-item-active-color: hsl(var(--primary-foreground));
--menu-item-active-background-color: hsl(var(--primary));
--menu-submenu-hover-color: hsl(var(--dark-foreground));
--menu-submenu-hover-background-color: hsl(var(--menu-dark-background));
--menu-submenu-active-color: hsl(var(--dark-foreground));
--menu-submenu-active-background-color: transparent;
--menu-submenu-background-color: var(--menu-background-color);
}
&.is-light {
--menu-background-color: hsl(var(--menu));
// --menu-submenu-opened-background-color: hsl(var(--menu-opened));
--menu-item-background-color: var(--menu-background-color);
--menu-item-color: hsl(var(--foreground));
--menu-item-hover-color: var(--menu-item-color);
--menu-item-hover-background-color: hsl(var(--menu-light-background));
--menu-item-active-color: hsl(var(--primary-foreground));
--menu-item-active-background-color: hsl(var(--primary));
--menu-submenu-hover-color: hsl(var(--primary));
--menu-submenu-hover-background-color: hsl(var(--menu-light-background));
--menu-submenu-active-color: hsl(var(--primary));
--menu-submenu-active-background-color: transparent;
--menu-submenu-background-color: var(--menu-background-color);
}
&.is-rounded {
--menu-item-margin-x: 8px;
--menu-item-collapse-margin-x: 6px;
--menu-item-radius: 6px;
}
&.is-horizontal:not(.is-rounded) {
--menu-item-height: 60px;
--menu-item-radius: 0px;
}
&.is-horizontal.is-rounded {
--menu-item-height: 40px;
--menu-item-radius: 6px;
--menu-item-padding-x: 12px;
}
// .vben-menu__popup,
&.is-horizontal {
--menu-item-padding-y: 0px;
--menu-item-padding-x: 10px;
--menu-item-margin-y: 0px;
--menu-item-margin-x: 1px;
--menu-background-color: transparent;
&.is-dark {
--menu-item-hover-color: var(--foreground);
--menu-item-hover-background-color: hsl(var(--menu-dark-background));
--menu-item-active-color: hsl(var(--foreground));
--menu-item-active-background-color: hsl(var(--menu-dark-background));
--menu-submenu-active-color: hsl(var(--foreground));
--menu-submenu-active-background-color: hsl(var(--menu-dark-background));
--menu-submenu-hover-color: hsl(var(--foreground));
--menu-submenu-hover-background-color: hsl(var(--menu-dark-background));
}
&.is-light {
--menu-item-active-color: hsl(var(--foreground));
--menu-item-active-background-color: hsl(var(--menu-light-background));
--menu-item-hover-background-color: hsl(var(--menu-light-background));
--menu-item-hover-color: hsl(var(--primary));
--menu-submenu-hover-color: hsl(var(--primary));
--menu-submenu-hover-background-color: hsl(var(--menu-light-background));
--menu-submenu-active-color: hsl(var(--foreground));
--menu-submenu-active-background-color: hsl(var(--menu-light-background));
}
}
}
.#{$namespace}-menu {
position: relative;
box-sizing: border-box;
padding-left: 0;
margin: 0;
list-style: none;
background: hsl(var(--menu-background-color));
// 垂直菜单
&.is-vertical {
&:not(.#{$namespace}-menu.is-collapse) {
& .#{$namespace}-menu-item,
& .#{$namespace}-sub-menu-content,
& .#{$namespace}-menu-item-group__title {
padding-left: calc(
var(--menu-item-indent) + var(--menu-level) * var(--menu-item-indent)
);
white-space: nowrap;
}
& > .#{$namespace}-sub-menu {
// .#{$namespace}-menu {
// background: var(--menu-submenu-opened-background-color);
// .#{$namespace}-sub-menu,
// .#{$namespace}-menu-item:not(.is-active),
// .#{$namespace}-sub-menu-content:not(.is-active) {
// background: var(--menu-submenu-opened-background-color);
// }
// }
& > .#{$namespace}-menu {
& > .#{$namespace}-menu-item {
padding-left: calc(
0px + var(--menu-item-indent) + var(--menu-level) *
var(--menu-item-indent)
);
}
}
& > .#{$namespace}-sub-menu-content {
padding-left: calc(var(--menu-item-indent) - 8px);
}
}
& > .#{$namespace}-menu-item {
padding-left: calc(var(--menu-item-indent) - 8px);
}
}
}
&.is-horizontal {
display: flex;
flex-wrap: nowrap;
max-width: 100%;
height: var(--height-horizontal-height);
border-right: none;
.#{$namespace}-menu-item {
display: inline-flex;
align-items: center;
justify-content: center;
height: var(--menu-item-height);
padding-right: calc(var(--menu-item-padding-x) + 6px);
margin: 0;
margin-right: 2px;
// border-bottom: 2px solid transparent;
border-radius: var(--menu-item-radius);
}
& > .#{$namespace}-sub-menu {
height: var(--menu-item-height);
margin-right: 2px;
&:focus,
&:hover {
outline: none;
}
& .#{$namespace}-sub-menu-content {
height: 100%;
padding-right: 40px;
// border-bottom: 2px solid transparent;
border-radius: var(--menu-item-radius);
}
}
& .#{$namespace}-menu-item:not(.is-disabled):hover,
& .#{$namespace}-menu-item:not(.is-disabled):focus {
outline: none;
}
& > .#{$namespace}-menu-item.is-active {
color: var(--menu-item-active-color);
}
// &.is-light {
// & > .#{$namespace}-sub-menu {
// &.is-active {
// border-bottom: 2px solid var(--menu-item-active-color);
// }
// &:not(.is-active) .#{$namespace}-sub-menu-content {
// &:hover {
// border-bottom: 2px solid var(--menu-item-active-color);
// }
// }
// }
// & > .#{$namespace}-menu-item.is-active {
// border-bottom: 2px solid var(--menu-item-active-color);
// }
// & .#{$namespace}-menu-item:not(.is-disabled):hover,
// & .#{$namespace}-menu-item:not(.is-disabled):focus {
// border-bottom: 2px solid var(--menu-item-active-color);
// }
// }
}
// 折叠菜单
&.is-collapse {
.#{$namespace}-menu__icon {
margin-right: 0;
}
.#{$namespace}-sub-menu__icon-arrow {
display: none;
}
.#{$namespace}-sub-menu-content,
.#{$namespace}-menu-item {
display: flex;
align-items: center;
justify-content: center;
padding: var(--menu-item-collapse-padding-y)
var(--menu-item-collapse-padding-x);
margin: var(--menu-item-collapse-margin-y)
var(--menu-item-collapse-margin-x);
transition: all 0.3s;
&.is-active {
background: var(--menu-item-active-background-color) !important;
border-radius: var(--menu-item-radius);
}
}
&.is-light {
.#{$namespace}-sub-menu-content,
.#{$namespace}-menu-item {
&.is-active {
color: hsl(var(--primary-foreground)) !important;
background: var(--menu-item-active-background-color) !important;
}
}
}
&.is-rounded {
.#{$namespace}-sub-menu-content,
.#{$namespace}-menu-item {
&.is-collapse-show-title {
// padding: 32px 0 !important;
margin: 4px 8px !important;
}
}
}
}
&__popup-container {
max-width: 240px;
height: unset;
padding: 0;
background: var(--menu-background-color);
}
&__popup {
padding: 4px 0;
border-radius: var(--menu-item-radius);
.#{$namespace}-sub-menu-content,
.#{$namespace}-menu-item {
padding: var(--menu-item-popup-padding-y) var(--menu-item-popup-padding-x);
}
}
&__icon {
flex-shrink: 0;
// width: var(--menu-item-icon-width);
max-height: var(--menu-item-icon-width);
margin-right: 12px;
font-size: 20px;
text-align: center;
vertical-align: middle;
}
}
.#{$namespace}-menu-item {
fill: var(--menu-item-color);
stroke: var(--menu-item-color);
@include menu-item;
&.is-active {
fill: var(--menu-item-active-color);
stroke: var(--menu-item-active-color);
@include menu-item-active;
}
&__content {
display: inline-flex;
align-items: center;
width: 100%;
height: var(--menu-item-height);
}
&.is-collapse-show-title {
padding: 32px 0 !important;
// margin: 4px 8px !important;
.#{$namespace}-menu-tooltip__trigger {
flex-direction: column;
}
.#{$namespace}-menu__icon {
display: block;
font-size: 20px !important;
transition: all 0.25s ease;
}
.#{$namespace}-menu__name {
display: inline-flex;
margin-top: 8px;
margin-bottom: 0;
font-size: 12px;
font-weight: 400;
line-height: normal;
transition: all 0.25s ease;
}
}
&:not(.is-active):hover {
color: var(--menu-item-hover-color);
text-decoration: none;
cursor: pointer;
background: var(--menu-item-hover-background-color) !important;
}
.#{$namespace}-menu-tooltip__trigger {
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 0 var(--menu-item-padding-x);
font-size: var(--menu-font-size);
line-height: var(--menu-item-height);
}
}
.#{$namespace}-sub-menu {
padding-left: 0;
margin: 0;
list-style: none;
background: var(--menu-submenu-background-color);
fill: var(--menu-item-color);
stroke: var(--menu-item-color);
&.is-active {
div[data-state='open'] > .#{$namespace}-sub-menu-content,
> .#{$namespace}-sub-menu-content {
color: var(--menu-submenu-active-color);
text-decoration: none;
cursor: pointer;
background: var(--menu-submenu-active-background-color);
fill: var(--menu-submenu-active-color);
stroke: var(--menu-submenu-active-color);
}
}
}
.#{$namespace}-sub-menu-content {
height: var(--menu-item-height);
@include menu-item;
&__icon-arrow {
position: absolute;
top: 50%;
right: 6px;
width: inherit;
margin-top: -8px;
margin-right: 0;
font-size: 16px;
font-weight: normal;
opacity: 1;
transition: transform 0.25s ease;
}
&__title {
@include menu-title;
}
&.is-collapse-show-title {
flex-direction: column;
padding: 32px 0 !important;
// margin: 4px 8px !important;
.#{$namespace}-menu__icon {
display: block;
font-size: 20px !important;
transition: all 0.25s ease;
}
.#{$namespace}-sub-menu-content__title {
display: inline-flex;
flex-shrink: 0;
margin-top: 8px;
margin-bottom: 0;
font-size: 12px;
font-weight: 400;
line-height: normal;
transition: all 0.25s ease;
}
}
&.is-more {
padding-right: 12px !important;
}
// &:not(.is-active):hover {
&:hover {
color: var(--menu-submenu-hover-color);
text-decoration: none;
cursor: pointer;
background: var(--menu-submenu-hover-background-color) !important;
svg {
fill: var(--menu-submenu-hover-color);
}
}
}
</style>

View File

@@ -3,8 +3,8 @@ import type { MenuRecordRaw } from '@vben-core/typings';
import type { NormalMenuProps } from './normal-menu';
import { useNamespace } from '@vben-core/hooks';
import { VbenIcon } from '@vben-core/shadcn-ui';
import { useNamespace } from '@vben-core/toolkit';
interface Props extends NormalMenuProps {}
@@ -58,9 +58,9 @@ function handleMouseenter(menu: MenuRecordRaw) {
</ul>
</template>
<style lang="scss" scoped>
@import '@vben-core/design/bem';
$namespace: vben;
@include b('normal-menu') {
.#{$namespace}-normal-menu {
--menu-item-margin-y: 4px;
--menu-item-margin-x: 0px;
--menu-item-padding-y: 11px;
@@ -70,18 +70,37 @@ function handleMouseenter(menu: MenuRecordRaw) {
height: calc(100% - 4px);
@include is('rounded') {
&.is-rounded {
--menu-item-radius: 6px;
--menu-item-margin-x: 8px;
}
@include is('dark') {
&.is-dark {
.#{$namespace}-normal-menu__item {
color: hsl(var(--dark-foreground) / 80%);
&:not(.is-active):hover {
color: hsl(var(--primary-foreground));
background-color: hsl(var(--menu-dark-background));
}
}
}
@include e('item') {
&.is-collapse {
.#{$namespace}-normal-menu__name {
width: 0;
height: 0;
margin-top: 0;
overflow: hidden;
opacity: 0;
}
.#{$namespace}-normal-menu__icon {
font-size: 20px;
}
}
&__item {
position: relative;
display: flex;
flex-direction: column;
@@ -100,7 +119,7 @@ function handleMouseenter(menu: MenuRecordRaw) {
padding 0.15s ease,
border-color 0.15s ease;
@include is('active') {
&.is-active {
font-weight: 700;
color: hsl(var(--primary-foreground));
background-color: hsl(var(--primary));
@@ -126,36 +145,13 @@ function handleMouseenter(menu: MenuRecordRaw) {
}
}
@include is('dark') {
.#{$namespace}-normal-menu__item {
&:not(.is-active):hover {
color: hsl(var(--primary-foreground));
background-color: hsl(var(--menu-dark-background));
}
}
}
@include is('collapse') {
.#{$namespace}-normal-menu__name {
width: 0;
height: 0;
margin-top: 0;
overflow: hidden;
opacity: 0;
}
.#{$namespace}-normal-menu__icon {
font-size: 20px;
}
}
@include e('icon') {
&__icon {
max-height: 20px;
font-size: 20px;
transition: all 0.25s ease;
}
@include e('name') {
&__name {
margin-top: 8px;
margin-bottom: 0;
font-size: 12px;

View File

@@ -3,12 +3,12 @@ import type { MenuItemProps } from '../interface';
import { computed } from 'vue';
import { useNamespace } from '@vben-core/hooks';
import {
IcRoundChevronRight,
IcRoundKeyboardArrowDown,
} from '@vben-core/iconify';
import { VbenIcon } from '@vben-core/shadcn-ui';
import { useNamespace } from '@vben-core/toolkit';
import { useMenuContext } from '../hooks';

View File

@@ -9,8 +9,8 @@ import type {
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
import { useNamespace } from '@vben-core/hooks';
import { VbenHoverCard } from '@vben-core/shadcn-ui';
import { useNamespace } from '@vben-core/toolkit';
import {
createSubMenuContext,

View File

@@ -1,5 +1,3 @@
import './styles/index.scss';
export * from './components/normal-menu';
export type * from './interface';
export { default as Menu } from './menu.vue';

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { MenuRecordRaw } from '@vben-core/typings';
import { useForwardProps } from '@vben-core/shadcn-ui';
import { useForwardProps } from '@vben-core/hooks';
import { Menu } from './components';
import { MenuProps } from './interface';

View File

@@ -1,4 +1,4 @@
@import '@vben-core/design/bem';
$namespace: vben;
.#{$namespace}-menu__popup-container,
.#{$namespace}-menu {
@@ -133,7 +133,7 @@
padding 0.15s ease,
border-color 0.15s ease;
@include is(disabled) {
&.is-disabled {
cursor: not-allowed;
background: none !important;
opacity: 0.25;
@@ -167,7 +167,7 @@
opacity: 1;
}
@include b('menu') {
.#{$namespace}-menu {
position: relative;
box-sizing: border-box;
padding-left: 0;
@@ -176,50 +176,47 @@
background: hsl(var(--menu-background-color));
// 垂直菜单
@include is('vertical') {
& {
&:not(.#{$namespace}-menu.is-collapse) {
& .#{$namespace}-menu-item,
& .#{$namespace}-sub-menu-content,
& .#{$namespace}-menu-item-group__title {
padding-left: calc(
var(--menu-item-indent) + var(--menu-level) *
var(--menu-item-indent)
);
white-space: nowrap;
}
&.is-vertical {
&:not(.#{$namespace}-menu.is-collapse) {
& .#{$namespace}-menu-item,
& .#{$namespace}-sub-menu-content,
& .#{$namespace}-menu-item-group__title {
padding-left: calc(
var(--menu-item-indent) + var(--menu-level) * var(--menu-item-indent)
);
white-space: nowrap;
}
& > .#{$namespace}-sub-menu {
// .#{$namespace}-menu {
// background: var(--menu-submenu-opened-background-color);
& > .#{$namespace}-sub-menu {
// .#{$namespace}-menu {
// background: var(--menu-submenu-opened-background-color);
// .#{$namespace}-sub-menu,
// .#{$namespace}-menu-item:not(.is-active),
// .#{$namespace}-sub-menu-content:not(.is-active) {
// background: var(--menu-submenu-opened-background-color);
// }
// }
& > .#{$namespace}-menu {
& > .#{$namespace}-menu-item {
padding-left: calc(
0px + var(--menu-item-indent) + var(--menu-level) *
var(--menu-item-indent)
);
}
}
& > .#{$namespace}-sub-menu-content {
padding-left: calc(var(--menu-item-indent) - 8px);
// .#{$namespace}-sub-menu,
// .#{$namespace}-menu-item:not(.is-active),
// .#{$namespace}-sub-menu-content:not(.is-active) {
// background: var(--menu-submenu-opened-background-color);
// }
// }
& > .#{$namespace}-menu {
& > .#{$namespace}-menu-item {
padding-left: calc(
0px + var(--menu-item-indent) + var(--menu-level) *
var(--menu-item-indent)
);
}
}
& > .#{$namespace}-menu-item {
& > .#{$namespace}-sub-menu-content {
padding-left: calc(var(--menu-item-indent) - 8px);
}
}
& > .#{$namespace}-menu-item {
padding-left: calc(var(--menu-item-indent) - 8px);
}
}
}
@include is('horizontal') {
&.is-horizontal {
display: flex;
flex-wrap: nowrap;
max-width: 100%;
@@ -287,7 +284,7 @@
}
// 折叠菜单
@include is('collapse') {
&.is-collapse {
.#{$namespace}-menu__icon {
margin-right: 0;
}
@@ -333,14 +330,14 @@
}
}
@include e('popup-container') {
&__popup-container {
max-width: 240px;
height: unset;
padding: 0;
background: var(--menu-background-color);
}
@include e('popup') {
&__popup {
padding: 4px 0;
border-radius: var(--menu-item-radius);
@@ -350,7 +347,7 @@
}
}
@include e('icon') {
&__icon {
flex-shrink: 0;
// width: var(--menu-item-icon-width);
max-height: var(--menu-item-icon-width);
@@ -361,27 +358,27 @@
}
}
@include b('menu-item') {
.#{$namespace}-menu-item {
fill: var(--menu-item-color);
stroke: var(--menu-item-color);
@include menu-item;
@include is(active) {
&.is-active {
fill: var(--menu-item-active-color);
stroke: var(--menu-item-active-color);
@include menu-item-active;
}
@include e('content') {
&__content {
display: inline-flex;
align-items: center;
width: 100%;
height: var(--menu-item-height);
}
@include is('collapse-show-title') {
&.is-collapse-show-title {
padding: 32px 0 !important;
// margin: 4px 8px !important;
.#{$namespace}-menu-tooltip__trigger {
@@ -427,7 +424,7 @@
}
}
@include b('sub-menu') {
.#{$namespace}-sub-menu {
padding-left: 0;
margin: 0;
list-style: none;
@@ -435,7 +432,7 @@
fill: var(--menu-item-color);
stroke: var(--menu-item-color);
@include is('active') {
&.is-active {
div[data-state='open'] > .#{$namespace}-sub-menu-content,
> .#{$namespace}-sub-menu-content {
color: var(--menu-submenu-active-color);
@@ -448,12 +445,12 @@
}
}
@include b('sub-menu-content') {
.#{$namespace}-sub-menu-content {
height: var(--menu-item-height);
@include menu-item;
@include e('icon-arrow') {
&__icon-arrow {
position: absolute;
top: 50%;
right: 6px;
@@ -466,11 +463,11 @@
transition: transform 0.25s ease;
}
@include e('title') {
&__title {
@include menu-title;
}
@include is('collapse-show-title') {
&.is-collapse-show-title {
flex-direction: column;
padding: 32px 0 !important;
// margin: 4px 8px !important;

View File

@@ -1,3 +0,0 @@
import { defineConfig } from '@vben/vite-config';
export default defineConfig();

View File

@@ -1,10 +1,4 @@
import './styles/index.css';
export * from './components';
export {
VisuallyHidden,
useEmitAsProps,
useForwardExpose,
useForwardProps,
useForwardPropsEmits,
} from 'radix-vue';
export { VisuallyHidden } from 'radix-vue';

View File

@@ -37,7 +37,6 @@
}
},
"dependencies": {
"@vben-core/design": "workspace:*",
"@vben-core/hooks": "workspace:*",
"@vben-core/iconify": "workspace:*",
"@vben-core/shadcn-ui": "workspace:*",

View File

@@ -1 +1,2 @@
export { default as Tabs } from './tabs/tabs.vue';
export { default as TabsChrome } from './tabs-chrome/tabs.vue';

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { TabItem } from '@vben-core/typings';
import type { TabDefinition } from '@vben-core/typings';
import type { TabsProps } from '../../types';
import type { TabConfig, TabsProps } from '../../types';
import { computed, nextTick, onMounted, ref, watch } from 'vue';
@@ -25,7 +25,7 @@ const props = withDefaults(defineProps<Props>(), {
tabs: () => [],
});
const emit = defineEmits<{ close: [string]; unpin: [TabItem] }>();
const emit = defineEmits<{ close: [string]; unpin: [TabDefinition] }>();
const active = defineModel<string>('active');
const contentRef = ref();
@@ -56,12 +56,14 @@ const layout = () => {
tabWidth.value = width;
};
const tabsView = computed(() => {
const tabsView = computed((): TabConfig[] => {
return props.tabs.map((tab) => {
return {
...tab,
affixTab: !!tab.meta?.affixTab,
closable: tab.meta?.tabClosable ?? true,
closable: Reflect.has(tab.meta, 'tabClosable')
? !!tab.meta.tabClosable
: true,
icon: tab.meta.icon as string,
key: tab.fullPath || tab.path,
title: (tab.meta?.title || tab.name) as string,
@@ -85,7 +87,8 @@ onMounted(() => {
function handleClose(key: string) {
emit('close', key);
}
function handleUnpinTab(tab: TabItem) {
function handleUnpinTab(tab: TabConfig) {
emit('unpin', tab);
}
</script>

View File

@@ -1,5 +0,0 @@
<script lang="ts" setup></script>
<template>
<div></div>
</template>

View File

@@ -0,0 +1,11 @@
<script lang="ts" setup>
import { VbenScrollbar } from '@vben-core/shadcn-ui';
</script>
<template>
<div class="bg-accent size-full">
<VbenScrollbar>
<slot></slot>
</VbenScrollbar>
</div>
</template>

View File

@@ -1,10 +1,9 @@
<script setup lang="ts">
import type { TabItem } from '@vben-core/typings';
import type { TabDefinition } from '@vben-core/typings';
import { nextTick, onMounted } from 'vue';
import { useSortable } from '@vben-core/hooks';
import { useForwardPropsEmits } from '@vben-core/shadcn-ui';
import { useForwardPropsEmits, useSortable } from '@vben-core/hooks';
import { TabsChrome } from './components';
import { TabsProps } from './types';
@@ -23,7 +22,7 @@ const props = withDefaults(defineProps<Props>(), {
const emit = defineEmits<{
close: [string];
sortTabs: [number, number];
unpin: [TabItem];
unpin: [TabDefinition];
}>();
const forward = useForwardPropsEmits(props, emit);

View File

@@ -1,5 +1,5 @@
import type { IContextMenuItem } from '@vben-core/shadcn-ui';
import type { TabItem } from '@vben-core/typings';
import type { TabDefinition } from '@vben-core/typings';
interface TabsProps {
/**
@@ -41,7 +41,15 @@ interface TabsProps {
/**
* @zh_CN 选项卡数据
*/
tabs?: TabItem[];
tabs?: TabDefinition[];
}
export type { TabsProps };
interface TabConfig extends TabDefinition {
affixTab: boolean;
closable: boolean;
icon: string;
key: string;
title: string;
}
export type { TabConfig, TabsProps };