Browse Source

feat: add swap component (#4149)

Vben 7 months ago
parent
commit
83fcdec37c
27 changed files with 260 additions and 146 deletions
  1. 4 2
      docs/src/guide/in-depth/theme.md
  2. 1 1
      internal/node-utils/src/spinner.ts
  3. 3 3
      packages/@core/base/design/src/design-tokens/dark/index.css
  4. 1 1
      packages/@core/base/design/src/design-tokens/default/index.css
  5. 1 1
      packages/@core/ui-kit/layout-ui/src/components/layout-header.vue
  6. 4 11
      packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue
  7. 41 0
      packages/@core/ui-kit/layout-ui/src/hooks/use-layout.ts
  8. 1 11
      packages/@core/ui-kit/layout-ui/src/vben-layout.ts
  9. 44 83
      packages/@core/ui-kit/layout-ui/src/vben-layout.vue
  10. 3 3
      packages/@core/ui-kit/menu-ui/src/components/menu.vue
  11. 1 0
      packages/@core/ui-kit/shadcn-ui/src/components/index.ts
  12. 1 1
      packages/@core/ui-kit/shadcn-ui/src/components/spinner/index.ts
  13. 1 0
      packages/@core/ui-kit/shadcn-ui/src/components/swap/index.ts
  14. 126 0
      packages/@core/ui-kit/shadcn-ui/src/components/swap/swap.vue
  15. 1 1
      packages/@core/ui-kit/shadcn-ui/src/components/ui/dialog/DialogScrollContent.vue
  16. 2 2
      packages/@core/ui-kit/shadcn-ui/src/components/ui/sheet/sheet.ts
  17. 1 1
      packages/@core/ui-kit/shadcn-ui/src/components/ui/toast/toast.ts
  18. 1 1
      packages/@core/ui-kit/shadcn-ui/src/components/ui/tooltip/TooltipContent.vue
  19. 1 1
      packages/constants/README.md
  20. 1 1
      packages/effects/hooks/README.md
  21. 2 2
      packages/effects/layouts/src/basic/content/content.vue
  22. 13 14
      packages/effects/layouts/src/basic/layout.vue
  23. 2 2
      packages/effects/layouts/src/iframe/iframe-router-view.vue
  24. 1 1
      packages/icons/README.md
  25. 1 1
      packages/styles/README.md
  26. 1 1
      packages/types/README.md
  27. 1 1
      packages/utils/README.md

+ 4 - 2
docs/src/guide/in-depth/theme.md

@@ -239,9 +239,11 @@ css 变量内的颜色,必须使用 `hsl` 格式,如 `0 0% 100%`,不需要
 
 ```css
 /*  */
-:root {
+.dark,
+.dark[data-theme='custom'],
+.dark[data-theme='default'] {
   /* Background color for <Card /> */
-  --card: 0 0% 30%;
+  --card: 222.34deg 10.43% 12.27%;
 }
 ```
 

+ 1 - 1
internal/node-utils/src/spinner.ts

@@ -1,4 +1,4 @@
-import ora, { Ora } from 'ora';
+import ora, { type Ora } from 'ora';
 
 interface SpinnerOptions {
   failedText?: string;

+ 3 - 3
packages/@core/base/design/src/design-tokens/dark/index.css

@@ -52,12 +52,12 @@
   --secondary-foreground: 0 0% 98%;
 
   /* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */
-  --accent: 240 3.7% 15.9%;
-  --accent-hover: 240 3.7% 20.9%;
+  --accent: 216 5% 19%;
+  --accent-hover: 216 5% 24%;
   --accent-foreground: 0 0% 98%;
 
   /* Darker color */
-  --heavy: 240 3.7% 20.9%;
+  --heavy: 216 5% 24%;
   --heavy-foreground: var(--accent-foreground);
 
   /* Default border color */

+ 1 - 1
packages/@core/base/design/src/design-tokens/default/index.css

@@ -89,7 +89,7 @@
 
   /* menu */
   --sidebar: 0 0% 100%;
-  --sidebar-deep: 210 11.11% 96.47%;
+  --sidebar-deep: 0 0% 100%;
   --menu: var(--sidebar);
 
   accent-color: var(--primary);

+ 1 - 1
packages/@core/ui-kit/layout-ui/src/components/layout-header.vue

@@ -84,7 +84,7 @@ function handleToggleMenu() {
     </div>
     <VbenIconButton
       v-if="showToggleBtn || isMobile"
-      class="my-0 ml-2 mr-1 rounded"
+      class="my-0 ml-2 mr-1 rounded-md"
       @click="handleToggleMenu"
     >
       <Menu class="size-4" />

+ 4 - 11
packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue

@@ -24,9 +24,8 @@ interface Props {
   domVisible?: boolean;
   /**
    * 扩展区域宽度
-   * @default 180
    */
-  extraWidth?: number;
+  extraWidth: number;
   /**
    * 固定扩展区域
    * @default false
@@ -69,13 +68,12 @@ interface Props {
   /**
    * 主题
    */
-  theme?: string;
+  theme: string;
 
   /**
    * 宽度
-   * @default 180
    */
-  width?: number;
+  width: number;
   /**
    * zIndex
    * @default 0
@@ -87,7 +85,6 @@ const props = withDefaults(defineProps<Props>(), {
   collapseHeight: 42,
   collapseWidth: 48,
   domVisible: true,
-  extraWidth: 180,
   fixedExtra: false,
   isSidebarMixed: false,
   marginTop: 0,
@@ -95,8 +92,6 @@ const props = withDefaults(defineProps<Props>(), {
   paddingTop: 0,
   show: true,
   showCollapseButton: true,
-  theme: 'dark',
-  width: 180,
   zIndex: 0,
 });
 
@@ -181,10 +176,8 @@ const extraContentStyle = computed((): CSSProperties => {
 });
 
 const collapseStyle = computed((): CSSProperties => {
-  const { collapseHeight } = props;
-
   return {
-    height: `${collapseHeight}px`,
+    height: `${props.collapseHeight}px`,
   };
 });
 

+ 41 - 0
packages/@core/ui-kit/layout-ui/src/hooks/use-layout.ts

@@ -0,0 +1,41 @@
+import type { LayoutType } from '@vben-core/typings';
+
+import type { VbenLayoutProps } from '../vben-layout';
+
+import { computed } from 'vue';
+
+export function useLayout(props: VbenLayoutProps) {
+  const currentLayout = computed(() =>
+    props.isMobile ? 'sidebar-nav' : (props.layout as LayoutType),
+  );
+
+  /**
+   * 是否全屏显示content,不需要侧边、底部、顶部、tab区域
+   */
+  const isFullContent = computed(() => currentLayout.value === 'full-content');
+
+  /**
+   * 是否侧边混合模式
+   */
+  const isSidebarMixedNav = computed(
+    () => currentLayout.value === 'sidebar-mixed-nav',
+  );
+
+  /**
+   * 是否为头部导航模式
+   */
+  const isHeaderNav = computed(() => currentLayout.value === 'header-nav');
+
+  /**
+   * 是否为混合导航模式
+   */
+  const isMixedNav = computed(() => currentLayout.value === 'mixed-nav');
+
+  return {
+    currentLayout,
+    isFullContent,
+    isHeaderNav,
+    isMixedNav,
+    isSidebarMixedNav,
+  };
+}

+ 1 - 11
packages/@core/ui-kit/layout-ui/src/vben-layout.ts

@@ -62,12 +62,6 @@ interface VbenLayoutProps {
    * @default 48
    */
   headerHeight?: number;
-  /**
-   * header高度增加高度
-   * 在顶部存在导航时,额外加高header高度
-   * @default 10
-   */
-  headerHeightOffset?: number;
   /**
    * 顶栏是否隐藏
    * @default false
@@ -133,11 +127,7 @@ interface VbenLayoutProps {
    * @default 80
    */
   sidebarMixedWidth?: number;
-  /**
-   * 侧边栏是否半深色
-   * @default false
-   */
-  sidebarSemiDark?: boolean;
+
   /**
    * 侧边栏
    * @default dark

+ 44 - 83
packages/@core/ui-kit/layout-ui/src/vben-layout.vue

@@ -13,6 +13,7 @@ import {
   LayoutSidebar,
   LayoutTabbar,
 } from './components';
+import { useLayout } from './hooks/use-layout';
 
 interface Props extends VbenLayoutProps {}
 
@@ -32,7 +33,6 @@ const props = withDefaults(defineProps<Props>(), {
   footerFixed: true,
   footerHeight: 32,
   headerHeight: 50,
-  headerHeightOffset: 10,
   headerHidden: false,
   headerMode: 'fixed',
   headerToggleSidebarButton: true,
@@ -43,7 +43,6 @@ const props = withDefaults(defineProps<Props>(), {
   sidebarExtraCollapsedWidth: 60,
   sidebarHidden: false,
   sidebarMixedWidth: 80,
-  sidebarSemiDark: true,
   sidebarTheme: 'dark',
   sidebarWidth: 180,
   sideCollapseWidth: 60,
@@ -73,57 +72,23 @@ const {
 
 const { y: mouseY } = useMouse({ target: contentRef, type: 'client' });
 
-const realLayout = computed(() =>
-  props.isMobile ? 'sidebar-nav' : props.layout,
-);
-
-/**
- * 是否全屏显示content,不需要侧边、底部、顶部、tab区域
- */
-const fullContent = computed(() => realLayout.value === 'full-content');
-
-/**
- * 是否侧边混合模式
- */
-const isSidebarMixedNav = computed(
-  () => realLayout.value === 'sidebar-mixed-nav',
-);
-
-/**
- * 是否为头部导航模式
- */
-const isHeaderNav = computed(() => realLayout.value === 'header-nav');
-
-/**
- * 是否为混合导航模式
- */
-const isMixedNav = computed(() => realLayout.value === 'mixed-nav');
+const {
+  currentLayout,
+  isFullContent,
+  isHeaderNav,
+  isMixedNav,
+  isSidebarMixedNav,
+} = useLayout(props);
 
 /**
  * 顶栏是否自动隐藏
  */
 const isHeaderAutoMode = computed(() => props.headerMode === 'auto');
 
-/**
- * header区域高度
- */
-const getHeaderHeight = computed(() => {
-  const { headerHeight, headerHeightOffset } = props;
-
-  // if (!headerVisible) {
-  //   return 0;
-  // }
-
-  // 顶部存在导航时,增加10
-  const offset = isMixedNav.value || isHeaderNav.value ? headerHeightOffset : 0;
-
-  return headerHeight + offset;
-});
-
 const headerWrapperHeight = computed(() => {
   let height = 0;
   if (props.headerVisible && !props.headerHidden) {
-    height += getHeaderHeight.value;
+    height += props.headerHeight;
   }
   if (props.tabbarEnable) {
     height += props.tabbarHeight;
@@ -151,8 +116,8 @@ const sidebarEnableState = computed(() => {
  * 侧边区域离顶部高度
  */
 const sidebarMarginTop = computed(() => {
-  const { isMobile } = props;
-  return isMixedNav.value && !isMobile ? getHeaderHeight.value : 0;
+  const { headerHeight, isMobile } = props;
+  return isMixedNav.value && !isMobile ? headerHeight : 0;
 });
 
 /**
@@ -195,30 +160,13 @@ const sidebarExtraWidth = computed(() => {
 /**
  * 是否侧边栏模式,包含混合侧边
  */
-const isSideMode = computed(() =>
-  ['mixed-nav', 'sidebar-mixed-nav', 'sidebar-nav'].includes(realLayout.value),
+const isSideMode = computed(
+  () =>
+    currentLayout.value === 'mixed-nav' ||
+    currentLayout.value === 'sidebar-mixed-nav' ||
+    currentLayout.value === 'sidebar-nav',
 );
 
-const showSidebar = computed(() => {
-  // if (isMixedNav.value && !props.sideHidden) {
-  //   return false;
-  // }
-  return isSideMode.value && sidebarEnable.value;
-});
-
-const sidebarFace = computed(() => {
-  const { sidebarSemiDark, sidebarTheme } = props;
-  const isDark = sidebarTheme === 'dark' || sidebarSemiDark;
-  return {
-    theme: isDark ? 'dark' : 'light',
-  };
-});
-
-/**
- * 遮罩可见性
- */
-const maskVisible = computed(() => !sidebarCollapse.value && props.isMobile);
-
 /**
  * header fixed值
  */
@@ -232,13 +180,25 @@ const headerFixed = computed(() => {
   );
 });
 
+const showSidebar = computed(() => {
+  // if (isMixedNav.value && !props.sideHidden) {
+  //   return false;
+  // }
+  return isSideMode.value && sidebarEnable.value;
+});
+
+/**
+ * 遮罩可见性
+ */
+const maskVisible = computed(() => !sidebarCollapse.value && props.isMobile);
+
 const mainStyle = computed(() => {
   let width = '100%';
   let sidebarAndExtraWidth = 'unset';
   if (
     headerFixed.value &&
-    realLayout.value !== 'header-nav' &&
-    realLayout.value !== 'mixed-nav' &&
+    currentLayout.value !== 'header-nav' &&
+    currentLayout.value !== 'mixed-nav' &&
     showSidebar.value &&
     !props.isMobile
   ) {
@@ -253,7 +213,7 @@ const mainStyle = computed(() => {
         ? getSideCollapseWidth.value
         : props.sidebarMixedWidth;
       const sideWidth = sidebarExtraCollapse.value
-        ? getSideCollapseWidth.value
+        ? props.sidebarExtraCollapsedWidth
         : props.sidebarWidth;
 
       // 100% - 侧边菜单混合宽度 - 菜单宽度
@@ -312,7 +272,7 @@ const contentStyle = computed((): CSSProperties => {
   return {
     marginTop:
       fixed &&
-      !fullContent.value &&
+      !isFullContent.value &&
       !headerIsHidden.value &&
       (!isHeaderAutoMode.value || scrollY.value < headerWrapperHeight.value)
         ? `${headerWrapperHeight.value}px`
@@ -330,11 +290,11 @@ const headerZIndex = computed(() => {
 const headerWrapperStyle = computed((): CSSProperties => {
   const fixed = headerFixed.value;
   return {
-    height: fullContent.value ? '0' : `${headerWrapperHeight.value}px`,
+    height: isFullContent.value ? '0' : `${headerWrapperHeight.value}px`,
     left: isMixedNav.value ? 0 : mainStyle.value.sidebarAndExtraWidth,
     position: fixed ? 'fixed' : 'static',
     top:
-      headerIsHidden.value || fullContent.value
+      headerIsHidden.value || isFullContent.value
         ? `-${headerWrapperHeight.value}px`
         : 0,
     width: mainStyle.value.width,
@@ -403,7 +363,10 @@ watch(
   watch(
     [() => props.headerMode, () => mouseY.value],
     () => {
-      if (!isHeaderAutoMode.value || isMixedNav.value || fullContent.value) {
+      if (!isHeaderAutoMode.value || isMixedNav.value || isFullContent.value) {
+        if (props.headerMode !== 'auto-scroll') {
+          headerIsHidden.value = false;
+        }
         return;
       }
       headerIsHidden.value = true;
@@ -439,7 +402,7 @@ watch(
       if (
         props.headerMode !== 'auto-scroll' ||
         isMixedNav.value ||
-        fullContent.value
+        isFullContent.value
       ) {
         return;
       }
@@ -465,8 +428,6 @@ function handleOpenMenu() {
 
 <template>
   <div class="relative flex min-h-full w-full">
-    <slot name="preferences"></slot>
-    <slot name="floating-groups"></slot>
     <LayoutSidebar
       v-if="sidebarEnableState"
       v-model:collapse="sidebarCollapse"
@@ -478,12 +439,12 @@ function handleOpenMenu() {
       :dom-visible="!isMobile"
       :extra-width="sidebarExtraWidth"
       :fixed-extra="sidebarExpandOnHover"
-      :header-height="isMixedNav ? 0 : getHeaderHeight"
+      :header-height="isMixedNav ? 0 : headerHeight"
       :is-sidebar-mixed="isSidebarMixedNav"
       :margin-top="sidebarMarginTop"
       :mixed-width="sidebarMixedWidth"
       :show="showSidebar"
-      :theme="sidebarFace.theme"
+      :theme="sidebarTheme"
       :width="getSidebarWidth"
       :z-index="sidebarZIndex"
       @leave="() => emit('sideMouseLeave')"
@@ -518,10 +479,10 @@ function handleOpenMenu() {
         <LayoutHeader
           v-if="headerVisible"
           :full-width="!isSideMode"
-          :height="getHeaderHeight"
+          :height="headerHeight"
           :is-mixed-nav="isMixedNav"
           :is-mobile="isMobile"
-          :show="!fullContent && !headerHidden"
+          :show="!isFullContent && !headerHidden"
           :show-toggle-btn="showHeaderToggleButton"
           :sidebar-width="sidebarWidth"
           :width="mainStyle.width"
@@ -563,7 +524,7 @@ function handleOpenMenu() {
         v-if="footerEnable"
         :fixed="footerFixed"
         :height="footerHeight"
-        :show="!fullContent"
+        :show="!isFullContent"
         :width="footerWidth"
         :z-index="zIndex"
       >

+ 3 - 3
packages/@core/ui-kit/menu-ui/src/components/menu.vue

@@ -479,8 +479,8 @@ $namespace: vben;
   }
 
   &.is-horizontal:not(.is-rounded) {
-    --menu-item-height: 60px;
-    --menu-item-radius: 0px;
+    --menu-item-height: 40px;
+    --menu-item-radius: 6px;
   }
 
   &.is-horizontal.is-rounded {
@@ -514,7 +514,7 @@ $namespace: vben;
       --menu-item-hover-background-color: hsl(var(--accent));
       --menu-item-hover-color: hsl(var(--primary));
       --menu-submenu-active-color: hsl(var(--primary));
-      --menu-submenu-active-background-color: hsl(var(--primary) / 30%);
+      --menu-submenu-active-background-color: hsl(var(--primary) / 15%);
       --menu-submenu-hover-color: hsl(var(--primary));
       --menu-submenu-hover-background-color: hsl(var(--accent));
     }

+ 1 - 0
packages/@core/ui-kit/shadcn-ui/src/components/index.ts

@@ -22,6 +22,7 @@ export * from './scrollbar';
 export * from './segmented';
 export * from './sheet';
 export * from './spinner';
+export * from './swap';
 export * from './tooltip';
 export * from './ui/alert-dialog';
 export * from './ui/avatar';

+ 1 - 1
packages/@core/ui-kit/shadcn-ui/src/components/spinner/index.ts

@@ -1 +1 @@
-export { default as Spinner } from './spinner.vue';
+export { default as VbenSpinner } from './spinner.vue';

+ 1 - 0
packages/@core/ui-kit/shadcn-ui/src/components/swap/index.ts

@@ -0,0 +1 @@
+export { default as VbenSwap } from './swap.vue';

+ 126 - 0
packages/@core/ui-kit/shadcn-ui/src/components/swap/swap.vue

@@ -0,0 +1,126 @@
+<script lang="ts" setup>
+interface Props {
+  /**
+   * @zh_CN 交换模式
+   */
+  mode?: 'flip' | 'rotate';
+  /**
+   * @zh_CN 开启时的样式
+   */
+  offClass?: string;
+  /**
+   * @zh_CN 关闭时的样式
+   */
+  onClass?: string;
+}
+
+defineOptions({
+  name: 'Swap',
+});
+
+withDefaults(defineProps<Props>(), {
+  mode: 'rotate',
+  onClass: '',
+});
+</script>
+
+<template>
+  <label
+    :class="{
+      'swap-flip': mode === 'flip',
+      'swap-rotate': mode === 'rotate',
+    }"
+    class="swap"
+  >
+    <input class="hidden" type="checkbox" />
+
+    <div :class="onClass" class="swap-on">
+      <slot name="swap-on"></slot>
+    </div>
+
+    <div :class="offClass" class="swap-off">
+      <slot name="swap-off"></slot>
+    </div>
+  </label>
+</template>
+
+<style scoped>
+.swap {
+  @apply relative inline-grid cursor-pointer select-none place-content-center;
+}
+
+.swap > * {
+  @apply col-start-1 row-start-1 duration-300 ease-out;
+
+  transition-property: transform, opacity;
+}
+
+.swap-rotate .swap-on,
+.swap-rotate .swap-indeterminate,
+.swap-rotate input:indeterminate ~ .swap-on {
+  @apply rotate-45;
+}
+
+.swap-rotate input:checked ~ .swap-off,
+.swap-active:where(.swap-rotate) .swap-off,
+.swap-rotate input:indeterminate ~ .swap-off {
+  @apply -rotate-45;
+}
+
+.swap-rotate input:checked ~ .swap-on,
+.swap-active:where(.swap-rotate) .swap-on,
+.swap-rotate input:indeterminate ~ .swap-indeterminate {
+  @apply rotate-0;
+}
+
+.swap-flip {
+  transform-style: preserve-3d;
+  perspective: 16em;
+}
+
+.swap-flip .swap-on,
+.swap-flip .swap-indeterminate,
+.swap-flip input:indeterminate ~ .swap-on {
+  @apply opacity-100;
+
+  transform: rotateY(180deg);
+  backface-visibility: hidden;
+}
+
+.swap-flip input:checked ~ .swap-off,
+.swap-active:where(.swap-flip) .swap-off,
+.swap-flip input:indeterminate ~ .swap-off {
+  @apply opacity-100;
+
+  transform: rotateY(-180deg);
+  backface-visibility: hidden;
+}
+
+.swap-flip input:checked ~ .swap-on,
+.swap-active:where(.swap-flip) .swap-on,
+.swap-flip input:indeterminate ~ .swap-indeterminate {
+  transform: rotateY(0deg);
+}
+
+.swap input {
+  @apply appearance-none;
+}
+
+.swap .swap-on,
+.swap .swap-indeterminate,
+.swap input:indeterminate ~ .swap-on {
+  @apply opacity-0;
+}
+
+.swap input:checked ~ .swap-off,
+.swap-active .swap-off,
+.swap input:indeterminate ~ .swap-off {
+  @apply opacity-0;
+}
+
+.swap input:checked ~ .swap-on,
+.swap-active .swap-on,
+.swap input:indeterminate ~ .swap-indeterminate {
+  @apply opacity-100;
+}
+</style>

+ 1 - 1
packages/@core/ui-kit/shadcn-ui/src/components/ui/dialog/DialogScrollContent.vue

@@ -30,7 +30,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
 <template>
   <DialogPortal>
     <DialogOverlay
-      class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 border-border fixed inset-0 z-[1000] grid place-items-center overflow-y-auto bg-black/80"
+      class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 border-border fixed inset-0 z-[1000] grid place-items-center overflow-y-auto border bg-black/80"
     >
       <DialogContent
         :class="

+ 2 - 2
packages/@core/ui-kit/shadcn-ui/src/components/ui/sheet/sheet.ts

@@ -9,10 +9,10 @@ export const sheetVariants = cva(
     variants: {
       side: {
         bottom:
-          'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
+          'inset-x-0 bottom-0 border-t border-border data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
         left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
         right:
-          'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
+          'inset-y-0 right-0 h-full w-3/4 border-l  data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
         top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
       },
     },

+ 1 - 1
packages/@core/ui-kit/shadcn-ui/src/components/ui/toast/toast.ts

@@ -5,7 +5,7 @@ import type { HTMLAttributes } from 'vue';
 import { cva, type VariantProps } from 'class-variance-authority';
 
 export const toastVariants = cva(
-  'group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full',
+  'group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border border-border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full',
   {
     defaultVariants: {
       variant: 'default',

+ 1 - 1
packages/@core/ui-kit/shadcn-ui/src/components/ui/tooltip/TooltipContent.vue

@@ -41,7 +41,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
       v-bind="{ ...forwarded, ...$attrs }"
       :class="
         cn(
-          'bg-accent text-accent-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 border-border z-[1000] overflow-hidden rounded-sm px-4 py-2 text-xs shadow-md',
+          'bg-accent text-accent-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 border-border shadow-float z-[1000] overflow-hidden rounded-sm border px-4 py-2 text-xs',
           props.class,
         )
       "

+ 1 - 1
packages/constants/README.md

@@ -9,7 +9,7 @@
 ```bash
 # 进入目标应用目录,例如 apps/xxxx-app
 # cd apps/xxxx-app
-pnpm add @vben/constants --workspace
+pnpm add @vben/constants
 ```
 
 ### 使用

+ 1 - 1
packages/effects/hooks/README.md

@@ -9,7 +9,7 @@
 ```bash
 # 进入目标应用目录,例如 apps/xxxx-app
 # cd apps/xxxx-app
-pnpm add @vben/hooks --workspace
+pnpm add @vben/hooks
 ```
 
 ### 使用

+ 2 - 2
packages/effects/layouts/src/basic/content/content.vue

@@ -10,7 +10,7 @@ import { RouterView } from 'vue-router';
 import { useContentHeight } from '@vben/hooks';
 import { preferences, usePreferences } from '@vben/preferences';
 import { storeToRefs, useTabbarStore } from '@vben/stores';
-import { Spinner } from '@vben-core/shadcn-ui';
+import { VbenSpinner } from '@vben-core/shadcn-ui';
 
 import { IFrameRouterView } from '../../iframe';
 import { useContentSpinner } from './use-content-spinner';
@@ -86,7 +86,7 @@ function transformComponent(
 
 <template>
   <div class="relative h-full">
-    <Spinner
+    <VbenSpinner
       v-if="preferences.transition.loading"
       :spinning="spinning"
       :style="contentStyles"

+ 13 - 14
packages/effects/layouts/src/basic/layout.vue

@@ -41,6 +41,7 @@ const {
   isSideMixedNav,
   layout,
   sidebarCollapsed,
+  theme,
 } = usePreferences();
 const userStore = useUserStore();
 const { updateWatermark } = useWatermark();
@@ -50,7 +51,7 @@ const headerMenuTheme = computed(() => {
   return isDark.value ? 'dark' : 'light';
 });
 
-const theme = computed(() => {
+const sidebarTheme = computed(() => {
   const dark = isDark.value || preferences.theme.semiDarkMenu;
   return dark ? 'dark' : 'light';
 });
@@ -170,8 +171,7 @@ const headerSlots = computed(() => {
     :sidebar-expand-on-hover="preferences.sidebar.expandOnHover"
     :sidebar-extra-collapse="preferences.sidebar.extraCollapse"
     :sidebar-hidden="preferences.sidebar.hidden"
-    :sidebar-semi-dark="preferences.theme.semiDarkMenu"
-    :sidebar-theme="theme"
+    :sidebar-theme="sidebarTheme"
     :sidebar-width="preferences.sidebar.width"
     :tabbar-enable="preferences.tabbar.enable"
     :tabbar-height="preferences.tabbar.height"
@@ -192,14 +192,6 @@ const headerSlots = computed(() => {
         updatePreferences({ sidebar: { extraCollapse: value } })
     "
   >
-    <template v-if="preferences.app.enablePreferences" #preferences>
-      <Preferences @clear-preferences-and-logout="clearPreferencesAndLogout" />
-    </template>
-
-    <template #floating-groups>
-      <VbenBackTop />
-    </template>
-
     <!-- logo -->
     <template #logo>
       <VbenLogo
@@ -256,7 +248,7 @@ const headerSlots = computed(() => {
         :default-active="sidebarActive"
         :menus="wrapperMenus(sidebarMenus)"
         :rounded="isMenuRounded"
-        :theme="theme"
+        :theme="sidebarTheme"
         mode="vertical"
         @select="handleMenuSelect"
       />
@@ -267,7 +259,7 @@ const headerSlots = computed(() => {
         :active-path="extraActiveMenu"
         :menus="wrapperMenus(headerMenus)"
         :rounded="isMenuRounded"
-        :theme="theme"
+        :theme="sidebarTheme"
         @default-select="handleDefaultSelect"
         @enter="handleMenuMouseEnter"
         @select="handleMixedMenuSelect"
@@ -280,7 +272,7 @@ const headerSlots = computed(() => {
         :collapse="preferences.sidebar.extraCollapse"
         :menus="wrapperMenus(extraMenus)"
         :rounded="isMenuRounded"
-        :theme="theme"
+        :theme="sidebarTheme"
       />
     </template>
     <template #side-extra-title>
@@ -325,6 +317,13 @@ const headerSlots = computed(() => {
       <Transition v-if="preferences.widget.lockScreen" name="slide-up">
         <slot v-if="lockStore.isLockScreen" name="lock-screen"></slot>
       </Transition>
+
+      <template v-if="preferences.app.enablePreferences">
+        <Preferences
+          @clear-preferences-and-logout="clearPreferencesAndLogout"
+        />
+      </template>
+      <VbenBackTop />
     </template>
   </VbenAdminLayout>
 </template>

+ 2 - 2
packages/effects/layouts/src/iframe/iframe-router-view.vue

@@ -6,7 +6,7 @@ import { useRoute } from 'vue-router';
 
 import { preferences } from '@vben/preferences';
 import { useTabbarStore } from '@vben/stores';
-import { Spinner } from '@vben-core/shadcn-ui';
+import { VbenSpinner } from '@vben-core/shadcn-ui';
 
 defineOptions({ name: 'IFrameRouterView' });
 
@@ -73,7 +73,7 @@ function showSpinning(index: number) {
         v-show="routeShow(item)"
         class="relative size-full"
       >
-        <Spinner :spinning="showSpinning(index)" />
+        <VbenSpinner :spinning="showSpinning(index)" />
         <iframe
           :src="item.meta.iframeSrc as string"
           class="size-full"

+ 1 - 1
packages/icons/README.md

@@ -9,7 +9,7 @@
 ```bash
 # 进入目标应用目录,例如 apps/xxxx-app
 # cd apps/xxxx-app
-pnpm add @vben/icons --workspace
+pnpm add @vben/icons
 ```
 
 ### 使用

+ 1 - 1
packages/styles/README.md

@@ -9,7 +9,7 @@
 ```bash
 # 进入目标应用目录,例如 apps/xxxx-app
 # cd apps/xxxx-app
-pnpm add @vben/styles --workspace
+pnpm add @vben/styles
 ```
 
 ### 使用

+ 1 - 1
packages/types/README.md

@@ -9,7 +9,7 @@
 ```bash
 # 进入目标应用目录,例如 apps/xxxx-app
 # cd apps/xxxx-app
-pnpm add @vben/types --workspace
+pnpm add @vben/types
 ```
 
 ### 使用

+ 1 - 1
packages/utils/README.md

@@ -9,7 +9,7 @@
 ```bash
 # 进入目标应用目录,例如 apps/xxxx-app
 # cd apps/xxxx-app
-pnpm add @vben/utils --workspace
+pnpm add @vben/utils
 ```
 
 ### 使用