From 874152dd05966746fc8f446d82dfebd70930c292 Mon Sep 17 00:00:00 2001 From: travor20814 Date: Mon, 22 Jul 2024 10:57:36 +0800 Subject: [PATCH] fix(react/utils): fix scroll lock layout shifting bug and export getScrollbarWidth function --- packages/react/src/index.ts | 1 + .../react/src/utils/get-scrollbar-width.ts | 27 +++++++++++++++++++ packages/react/src/utils/scroll-lock.ts | 10 +++++-- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 packages/react/src/utils/get-scrollbar-width.ts diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index c6a099ef..c2e43b22 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -6,6 +6,7 @@ export * from './utils/jsx-types'; export * from './utils/general'; export * from './utils/scroll-lock'; export * from './utils/array-move'; +export * from './utils/get-scrollbar-width'; export * from './hooks/useClickAway'; export * from './hooks/useComposeRefs'; diff --git a/packages/react/src/utils/get-scrollbar-width.ts b/packages/react/src/utils/get-scrollbar-width.ts new file mode 100644 index 00000000..4167d929 --- /dev/null +++ b/packages/react/src/utils/get-scrollbar-width.ts @@ -0,0 +1,27 @@ +export default function getScrollbarWidth(): number { + // Create a temporary div container and append it into the body + const container = document.createElement('div'); + + // Append the element into the body + document.body.appendChild(container); + + // Force scrollbar on the temporary div element + container.style.overflow = 'scroll'; + container.style.width = '100px'; + container.style.height = '100px'; + + // Add a fake inner element to get the scrollbar width + const inner = document.createElement('div'); + + inner.style.width = '100%'; + inner.style.height = '100%'; + container.appendChild(inner); + + // Calculate the width based on the container width minus its child width + const scrollbarWidth = container.offsetWidth - inner.offsetWidth; + + // Remove the temporary elements from the DOM + document.body.removeChild(container); + + return scrollbarWidth; +} \ No newline at end of file diff --git a/packages/react/src/utils/scroll-lock.ts b/packages/react/src/utils/scroll-lock.ts index 7f1b26ac..b8bf4e8a 100644 --- a/packages/react/src/utils/scroll-lock.ts +++ b/packages/react/src/utils/scroll-lock.ts @@ -1,3 +1,5 @@ +import getScrollbarWidth from './get-scrollbar-width'; + export function lockBodyScroll() { const { scrollY, @@ -5,9 +7,12 @@ export function lockBodyScroll() { document.body.style.position = 'fixed'; document.body.style.top = `-${scrollY}px`; + + // Calculate scroll bar width and use padding-right to remain layout width. + const scrollbarWidth = getScrollbarWidth(); + + document.body.style.paddingRight = `${scrollbarWidth}px`; document.body.style.overflow = 'hidden'; - /** @NOTE workaround for layout breaking (need refactor) */ - document.body.style.width = '100vw'; } export function allowBodyScroll() { @@ -16,6 +21,7 @@ export function allowBodyScroll() { if (document.body.style.position === 'fixed') { document.body.style.position = ''; document.body.style.top = ''; + document.body.style.paddingRight = ''; document.body.style.overflow = ''; window.scrollTo(0, (scrollY || 0) * -1); }