diff --git a/packages/@core/base/design/src/css/global.css b/packages/@core/base/design/src/css/global.css index f9810e72b8a..3c1841091bc 100644 --- a/packages/@core/base/design/src/css/global.css +++ b/packages/@core/base/design/src/css/global.css @@ -37,10 +37,10 @@ } body { - @apply !pointer-events-auto; - min-height: 100vh; + /* pointer-events: auto !important; */ + /* overflow: overlay; */ /* -webkit-font-smoothing: antialiased; */ diff --git a/packages/@core/base/shared/src/utils/dom.ts b/packages/@core/base/shared/src/utils/dom.ts index 79640d60923..16d6ddef7ac 100644 --- a/packages/@core/base/shared/src/utils/dom.ts +++ b/packages/@core/base/shared/src/utils/dom.ts @@ -50,3 +50,22 @@ export function getElementVisibleRect( width: Math.max(0, right - left), }; } + +export function getScrollbarWidth() { + const scrollDiv = document.createElement('div'); + + scrollDiv.style.visibility = 'hidden'; + scrollDiv.style.overflow = 'scroll'; + scrollDiv.style.position = 'absolute'; + scrollDiv.style.top = '-9999px'; + + document.body.append(scrollDiv); + + const innerDiv = document.createElement('div'); + scrollDiv.append(innerDiv); + + const scrollbarWidth = scrollDiv.offsetWidth - innerDiv.offsetWidth; + + scrollDiv.remove(); + return scrollbarWidth; +} diff --git a/packages/@core/composables/src/index.ts b/packages/@core/composables/src/index.ts index 31a7feabcc4..abfd9501f52 100644 --- a/packages/@core/composables/src/index.ts +++ b/packages/@core/composables/src/index.ts @@ -2,6 +2,7 @@ export * from './use-content-style'; export * from './use-is-mobile'; export * from './use-namespace'; export * from './use-priority-value'; +export * from './use-scroll-lock'; export * from './use-simple-locale'; export * from './use-sortable'; export { diff --git a/packages/@core/composables/src/use-scroll-lock.ts b/packages/@core/composables/src/use-scroll-lock.ts new file mode 100644 index 00000000000..b3f40d6bcfd --- /dev/null +++ b/packages/@core/composables/src/use-scroll-lock.ts @@ -0,0 +1,48 @@ +import { getScrollbarWidth } from '@vben-core/shared/utils'; + +import { + useScrollLock as _useScrollLock, + tryOnBeforeMount, + tryOnBeforeUnmount, +} from '@vueuse/core'; + +export const SCROLL_FIXED_CLASS = `_scroll__fixed_`; + +export function useScrollLock() { + const isLocked = _useScrollLock(document.body); + const scrollbarWidth = getScrollbarWidth(); + + tryOnBeforeMount(() => { + document.body.style.paddingRight = `${scrollbarWidth}px`; + + const layoutFixedNodes = document.querySelectorAll( + `.${SCROLL_FIXED_CLASS}`, + ); + const nodes = [...layoutFixedNodes]; + if (nodes.length > 0) { + nodes.forEach((node) => { + node.dataset.transition = node.style.transition; + node.style.transition = 'none'; + node.style.paddingRight = `${scrollbarWidth}px`; + }); + } + isLocked.value = true; + }); + + tryOnBeforeUnmount(() => { + isLocked.value = false; + const layoutFixedNodes = document.querySelectorAll( + `.${SCROLL_FIXED_CLASS}`, + ); + const nodes = [...layoutFixedNodes]; + if (nodes.length > 0) { + nodes.forEach((node) => { + node.style.paddingRight = ''; + requestAnimationFrame(() => { + node.style.transition = node.dataset.transition || ''; + }); + }); + } + document.body.style.paddingRight = ''; + }); +} diff --git a/packages/@core/ui-kit/layout-ui/src/vben-layout.vue b/packages/@core/ui-kit/layout-ui/src/vben-layout.vue index 67c7278ae9f..0935077548a 100644 --- a/packages/@core/ui-kit/layout-ui/src/vben-layout.vue +++ b/packages/@core/ui-kit/layout-ui/src/vben-layout.vue @@ -4,6 +4,7 @@ import type { VbenLayoutProps } from './vben-layout'; import type { CSSProperties } from 'vue'; import { computed, ref, watch } from 'vue'; +import { SCROLL_FIXED_CLASS } from '@vben-core/composables'; import { Menu } from '@vben-core/icons'; import { VbenIconButton } from '@vben-core/shadcn-ui'; @@ -478,9 +479,12 @@ function handleHeaderToggle() { class="flex flex-1 flex-col overflow-hidden transition-all duration-300 ease-in" >
diff --git a/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts b/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts index ee00259241b..15ce8a9ee85 100644 --- a/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts +++ b/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts @@ -39,6 +39,7 @@ export class DrawerApi { isOpen: false, loading: false, modal: true, + openAutoFocus: false, showCancelButton: true, showConfirmButton: true, title: '', diff --git a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts index c3db0606962..df179830552 100644 --- a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts +++ b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts @@ -52,6 +52,10 @@ export interface DrawerProps { * @default true */ modal?: boolean; + /** + * 是否自动聚焦 + */ + openAutoFocus?: boolean; /** * 是否显示取消按钮 * @default true diff --git a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue index 4c441d98a04..025ca7420e5 100644 --- a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue +++ b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue @@ -51,6 +51,7 @@ const { footer: showFooter, loading: showLoading, modal, + openAutoFocus, showCancelButton, showConfirmButton, title, @@ -87,10 +88,21 @@ function pointerDownOutside(e: Event) { e.preventDefault(); } } + +function handerOpenAutoFocus(e: Event) { + if (!openAutoFocus.value) { + e?.preventDefault(); + } +} + +function handleFocusOutside(e: Event) { + e.preventDefault(); + e.stopPropagation(); +}