docs: fix irregular scrolling in code tabs (#5239)
* docs: fix blocking scroll util * remove unnecessary util methods
This commit is contained in:
1 parent
4e3f3b54cb
commit
11b2f1237b
4 files changed
+22
-78
No files matched your search
@@ -26,7 +26,7 @@ const Providers = ({ children }: ProvidersProps) => {
|
||||
<BaseSpecsProvider>
|
||||
<SidebarProvider>
|
||||
<NavbarProvider>
|
||||
<ScrollControllerProvider>
|
||||
<ScrollControllerProvider scrollableSelector="#main">
|
||||
<SearchProvider>
|
||||
<MobileProvider>{children}</MobileProvider>
|
||||
</SearchProvider>
|
||||
|
||||
@@ -35,7 +35,10 @@ export default function RootLayout({
|
||||
>
|
||||
<Providers>
|
||||
<Navbar />
|
||||
<div className="w-full h-[calc(100%-57px)] overflow-y-scroll">
|
||||
<div
|
||||
className="w-full h-[calc(100%-57px)] overflow-y-scroll"
|
||||
id="main"
|
||||
>
|
||||
<div className="max-w-xxl grid w-full grid-cols-1 px-6 lg:mx-auto lg:grid-cols-[280px_1fr]">
|
||||
<Sidebar expandItems={true} />
|
||||
<div className="relative flex w-full flex-1 items-start justify-center px-4 pb-8 pt-16 md:px-8 lg:px-16 lg:py-[112px]">
|
||||
|
||||
@@ -24,7 +24,7 @@ const Providers = ({ children }: ProvidersProps) => {
|
||||
<SidebarProvider>
|
||||
<NavbarProvider basePath={process.env.NEXT_PUBLIC_BASE_PATH}>
|
||||
<SearchProvider>
|
||||
<ScrollControllerProvider>
|
||||
<ScrollControllerProvider scrollableSelector="#main">
|
||||
{children}
|
||||
</ScrollControllerProvider>
|
||||
</SearchProvider>
|
||||
|
||||
@@ -53,11 +53,19 @@ type ScrollController = {
|
||||
enableScrollEvents: () => void
|
||||
/** Disable scroll events in `useScrollPosition`. */
|
||||
disableScrollEvents: () => void
|
||||
/** Retrieves the scrollable element. By default, it's window. */
|
||||
getScrollableElement: () => Element | Window
|
||||
}
|
||||
|
||||
function useScrollControllerContextValue(): ScrollController {
|
||||
function useScrollControllerContextValue(
|
||||
scrollableSelector: string
|
||||
): ScrollController {
|
||||
const scrollEventsEnabledRef = useRef(true)
|
||||
|
||||
const getScrollableElement = useCallback(() => {
|
||||
return (document.querySelector(scrollableSelector) as Element) || window
|
||||
}, [scrollableSelector])
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
scrollEventsEnabledRef,
|
||||
@@ -67,8 +75,9 @@ function useScrollControllerContextValue(): ScrollController {
|
||||
disableScrollEvents: () => {
|
||||
scrollEventsEnabledRef.current = false
|
||||
},
|
||||
getScrollableElement,
|
||||
}),
|
||||
[]
|
||||
[getScrollableElement]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -78,10 +87,12 @@ const ScrollMonitorContext = React.createContext<ScrollController | undefined>(
|
||||
|
||||
export function ScrollControllerProvider({
|
||||
children,
|
||||
scrollableSelector = "",
|
||||
}: {
|
||||
children: ReactNode
|
||||
scrollableSelector?: string
|
||||
}): JSX.Element {
|
||||
const value = useScrollControllerContextValue()
|
||||
const value = useScrollControllerContextValue(scrollableSelector)
|
||||
return (
|
||||
<ScrollMonitorContext.Provider value={value}>
|
||||
{children}
|
||||
@@ -165,6 +176,7 @@ type UseScrollPositionSaver = {
|
||||
}
|
||||
|
||||
function useScrollPositionSaver(): UseScrollPositionSaver {
|
||||
const { getScrollableElement } = useScrollController()
|
||||
const lastElementRef = useRef<{ elem: HTMLElement | null; top: number }>({
|
||||
elem: null,
|
||||
top: 0,
|
||||
@@ -187,7 +199,7 @@ function useScrollPositionSaver(): UseScrollPositionSaver {
|
||||
const newTop = elem.getBoundingClientRect().top
|
||||
const heightDiff = newTop - top
|
||||
if (heightDiff) {
|
||||
window.scrollBy({ left: 0, top: heightDiff })
|
||||
getScrollableElement().scrollBy({ left: 0, top: heightDiff })
|
||||
}
|
||||
lastElementRef.current = { elem: null, top: 0 }
|
||||
|
||||
@@ -258,74 +270,3 @@ export function useScrollPositionBlocker(): {
|
||||
blockElementScrollPositionUntilNextRender,
|
||||
}
|
||||
}
|
||||
|
||||
type CancelScrollTop = () => void
|
||||
|
||||
function smoothScrollNative(top: number): CancelScrollTop {
|
||||
window.scrollTo({ top, behavior: "smooth" })
|
||||
return () => {
|
||||
// Nothing to cancel, it's natively cancelled if user tries to scroll down
|
||||
}
|
||||
}
|
||||
|
||||
function smoothScrollPolyfill(top: number): CancelScrollTop {
|
||||
let raf: number | null = null
|
||||
const isUpScroll = document.documentElement.scrollTop > top
|
||||
function rafRecursion() {
|
||||
const currentScroll = document.documentElement.scrollTop
|
||||
if (
|
||||
(isUpScroll && currentScroll > top) ||
|
||||
(!isUpScroll && currentScroll < top)
|
||||
) {
|
||||
raf = requestAnimationFrame(rafRecursion)
|
||||
window.scrollTo(0, Math.floor((currentScroll - top) * 0.85) + top)
|
||||
}
|
||||
}
|
||||
rafRecursion()
|
||||
|
||||
// Break the recursion. Prevents the user from "fighting" against that
|
||||
// recursion producing a weird UX
|
||||
return () => raf && cancelAnimationFrame(raf)
|
||||
}
|
||||
|
||||
/**
|
||||
* A "smart polyfill" of `window.scrollTo({ top, behavior: "smooth" })`.
|
||||
* This currently always uses a polyfilled implementation unless
|
||||
* `scroll-behavior: smooth` has been set in CSS, because native support
|
||||
* detection for scroll behavior seems unreliable.
|
||||
*
|
||||
* This hook does not do anything by itself: it returns a start and a stop
|
||||
* handle. You can execute either handle at any time.
|
||||
*/
|
||||
export function useSmoothScrollTo(): {
|
||||
/**
|
||||
* Start the scroll.
|
||||
*
|
||||
* @param top The final scroll top position.
|
||||
*/
|
||||
startScroll: (top: number) => void
|
||||
/**
|
||||
* A cancel function, because the non-native smooth scroll-top
|
||||
* implementation must be interrupted if user scrolls down. If there's no
|
||||
* existing animation or the scroll is using native behavior, this is a no-op.
|
||||
*/
|
||||
cancelScroll: CancelScrollTop
|
||||
} {
|
||||
const cancelRef = useRef<CancelScrollTop | null>(null)
|
||||
// Not all have support for smooth scrolling (particularly Safari mobile iOS)
|
||||
// TODO proper detection is currently unreliable!
|
||||
// see https://github.com/wessberg/scroll-behavior-polyfill/issues/16
|
||||
// For now, we only use native scroll behavior if smooth is already set,
|
||||
// because otherwise the polyfill produces a weird UX when both CSS and JS try
|
||||
// to scroll a page, and they cancel each other.
|
||||
const supportsNativeSmoothScrolling =
|
||||
getComputedStyle(document.documentElement).scrollBehavior === "smooth"
|
||||
return {
|
||||
startScroll: (top: number) => {
|
||||
cancelRef.current = supportsNativeSmoothScrolling
|
||||
? smoothScrollNative(top)
|
||||
: smoothScrollPolyfill(top)
|
||||
},
|
||||
cancelScroll: () => cancelRef.current?.(),
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user