From 84e13f2064e58017efd0b8d8a45c1561f7d126d2 Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Thu, 12 Oct 2023 09:48:09 +0100 Subject: [PATCH] Include scrollbar width in media calculation Fix #697 --- app/scripts/utils/use-media-query.ts | 7 +++++- app/scripts/utils/use-scrollbar-width-css.ts | 25 ++++++++++++++------ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/app/scripts/utils/use-media-query.ts b/app/scripts/utils/use-media-query.ts index 3f88331c5..dbf8daead 100644 --- a/app/scripts/utils/use-media-query.ts +++ b/app/scripts/utils/use-media-query.ts @@ -2,6 +2,7 @@ import { useEffect, useMemo } from 'react'; import { useTheme } from 'styled-components'; import useDimensions from 'react-cool-dimensions'; import { DevseedUIThemeMediaRanges } from '@devseed-ui/theme-provider'; +import { getScrollbarWidth } from './use-scrollbar-width-css'; interface MediaBreakpointStatus { current: keyof DevseedUIThemeMediaRanges; @@ -82,11 +83,15 @@ export function useMediaQuery() { observe(document.body); }, [observe]); + // Account for the scrollbar width because the css media queries will. + const scrollbarWidth = getScrollbarWidth(); + // On first mount react-cool-dimension will return a width of 0, which breaks // the media queries styles because there's a mismatch between the css media // queries and the js. const width = - calculatedWidth || (typeof window !== 'undefined' ? window.innerWidth : 0); + calculatedWidth + scrollbarWidth || + (typeof window !== 'undefined' ? window.innerWidth + scrollbarWidth : 0); const rangeBooleans = useMemo( () => diff --git a/app/scripts/utils/use-scrollbar-width-css.ts b/app/scripts/utils/use-scrollbar-width-css.ts index f5fb06624..8dac28a4a 100644 --- a/app/scripts/utils/use-scrollbar-width-css.ts +++ b/app/scripts/utils/use-scrollbar-width-css.ts @@ -1,16 +1,27 @@ import { useEffect } from 'react'; +let scrollbarWidthCache; +export function getScrollbarWidth() { + if (scrollbarWidthCache !== undefined) { + return scrollbarWidthCache; + } + + const el = document.createElement('div'); + el.style.cssText = 'overflow:scroll; visibility:hidden; position:absolute;'; + document.body.appendChild(el); + const width = el.offsetWidth - el.clientWidth; + el.remove(); + + scrollbarWidthCache = width; + return width; +} + export function useScrollbarWidthAsCssVar(varName = '--scrollbar-width') { useEffect(() => { - const el = document.createElement('div'); - el.style.cssText = 'overflow:scroll; visibility:hidden; position:absolute;'; - document.body.appendChild(el); - const width = el.offsetWidth - el.clientWidth; - el.remove(); - + const width = getScrollbarWidth(); document.documentElement.style.setProperty(varName, width + 'px'); - () => { + return () => { document.documentElement.style.removeProperty(varName); }; }, [varName]);