From fd76852db391d371aa7621770fd62593cbc8f625 Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Wed, 31 Jul 2024 16:26:25 +0100 Subject: [PATCH] fix: allow full-width TOC when sidebar is useable --- packages/providers/src/ui.tsx | 10 +++++-- .../components/Navigation/TableOfContents.tsx | 11 ++++++-- themes/book/app/routes/$.tsx | 28 +++++++++++++++++-- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/providers/src/ui.tsx b/packages/providers/src/ui.tsx index 31c50b9e8..238e4152a 100644 --- a/packages/providers/src/ui.tsx +++ b/packages/providers/src/ui.tsx @@ -3,11 +3,12 @@ import { useMediaQuery } from './hooks.js'; type UiState = { isNavOpen?: boolean; + isWide?: boolean; }; type UiContextType = [UiState, (state: UiState) => void]; -const UiContext = createContext(undefined); +export const UiContext = createContext(undefined); // Create a provider for components to consume and subscribe to changes export function UiStateProvider({ children }: { children: React.ReactNode }) { @@ -15,7 +16,7 @@ export function UiStateProvider({ children }: { children: React.ReactNode }) { const wide = useMediaQuery('(min-width: 1280px)'); const [state, setState] = useState({ isNavOpen: false }); useEffect(() => { - if (wide) setState({ ...state, isNavOpen: false }); + if (wide) setState({ ...state, isNavOpen: false, isWide: wide }); }, [wide]); return {children}; } @@ -28,3 +29,8 @@ export function useNavOpen(): [boolean, (open: boolean) => void] { }; return [state?.isNavOpen ?? false, setOpen]; } + +export function isWide(): boolean { + const [state, _] = useContext(UiContext) ?? []; + return state?.isWide ?? false; +} diff --git a/packages/site/src/components/Navigation/TableOfContents.tsx b/packages/site/src/components/Navigation/TableOfContents.tsx index d2f2fa541..6d0f0c707 100644 --- a/packages/site/src/components/Navigation/TableOfContents.tsx +++ b/packages/site/src/components/Navigation/TableOfContents.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useRef, useContext } from 'react'; import classNames from 'classnames'; import { useNavigation } from '@remix-run/react'; import { @@ -6,6 +6,7 @@ import { useSiteManifest, useGridSystemProvider, useThemeTop, + isWide, } from '@myst-theme/providers'; import { getProjectHeadings } from '@myst-theme/common'; import { Toc } from './TableOfContentsItems.js'; @@ -14,11 +15,15 @@ export function useTocHeight(top = 0, inset const container = useRef(null); const toc = useRef(null); const transitionState = useNavigation().state; + const wide = isWide(); const setHeight = () => { if (!container.current || !toc.current) return; const height = container.current.offsetHeight - window.scrollY; const div = toc.current.firstChild as HTMLDivElement; - if (div) div.style.height = `min(calc(100vh - ${top}px), ${height + inset}px)`; + if (div) + div.style.height = wide + ? `min(calc(100vh - ${top}px), ${height + inset}px)` + : `calc(100vh - ${top}px)`; const nav = toc.current.querySelector('nav'); if (nav) nav.style.opacity = height > 150 ? '1' : '0'; }; @@ -30,7 +35,7 @@ export function useTocHeight(top = 0, inset return () => { window.removeEventListener('scroll', handleScroll); }; - }, [container, toc, transitionState]); + }, [container, toc, transitionState, wide]); return { container, toc }; } diff --git a/themes/book/app/routes/$.tsx b/themes/book/app/routes/$.tsx index a12345dc2..74e698061 100644 --- a/themes/book/app/routes/$.tsx +++ b/themes/book/app/routes/$.tsx @@ -71,7 +71,7 @@ export const loader: LoaderFunction = async ({ params, request }) => { return json({ config, page, project }); }; -export function ArticlePageAndNavigation({ +function ArticlePageAndNavigationInternal({ children, hide_toc, projectSlug, @@ -85,7 +85,7 @@ export function ArticlePageAndNavigation({ const top = useThemeTop(); const { container, toc } = useTocHeight(top, inset); return ( - + <> - + ); } +export function ArticlePageAndNavigation({ + children, + hide_toc, + projectSlug, + inset = 20, // begin text 20px from the top (aligned with menu) +}: { + hide_toc?: boolean; + projectSlug?: string; + children: React.ReactNode; + inset?: number; +}) { + return ( + + + + ); +} export default function Page() { const { container } = useOutlineHeight();